mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
add-cluster page -- part 1
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
2a818601f9
commit
5b98674d80
@ -41,6 +41,10 @@ msgstr "<0>Your browser does not support all Lens features. </0> Please consider
|
||||
msgid "<0>{0}</0> successfully created"
|
||||
msgstr "<0>{0}</0> successfully created"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:52
|
||||
msgid "A HTTP proxy server URL (format: http://<address>:<port>)"
|
||||
msgstr "A HTTP proxy server URL (format: http://<address>:<port>)"
|
||||
|
||||
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:80
|
||||
msgid "API Group"
|
||||
msgstr "API Group"
|
||||
@ -63,6 +67,10 @@ msgstr "Account Name"
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:44
|
||||
msgid "Add Cluster"
|
||||
msgstr "Add Cluster"
|
||||
|
||||
#: src/renderer/components/+namespaces/namespaces.tsx:43
|
||||
msgid "Add Namespace"
|
||||
msgstr "Add Namespace"
|
||||
@ -75,6 +83,10 @@ msgstr "Add RoleBinding"
|
||||
msgid "Add bindings to {name}"
|
||||
msgstr "Add bindings to {name}"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:62
|
||||
msgid "Add cluster"
|
||||
msgstr "Add cluster"
|
||||
|
||||
#: src/renderer/components/+workspaces/clusters-menu.tsx:58
|
||||
#~ msgid "Add clusters"
|
||||
#~ msgstr "Add clusters"
|
||||
@ -613,11 +625,19 @@ msgstr "Current replica scale: {currentReplicas}"
|
||||
msgid "Currently applied filters:"
|
||||
msgstr "Currently applied filters:"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:36
|
||||
#~ msgid "Custom"
|
||||
#~ msgstr "Custom"
|
||||
|
||||
#: src/renderer/components/+custom-resources/crd-list.tsx:55
|
||||
#: src/renderer/components/layout/sidebar.tsx:95
|
||||
msgid "Custom Resources"
|
||||
msgstr "Custom Resources"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:36
|
||||
msgid "Custom.."
|
||||
msgstr "Custom.."
|
||||
|
||||
#: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:95
|
||||
msgid "DNS Provider"
|
||||
msgstr "DNS Provider"
|
||||
@ -862,6 +882,10 @@ msgstr "Groups"
|
||||
msgid "HPA"
|
||||
msgstr "HPA"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:54
|
||||
msgid "HTTP Proxy server. Used for communicating with Kubernetes API."
|
||||
msgstr "HTTP Proxy server. Used for communicating with Kubernetes API."
|
||||
|
||||
#: src/renderer/components/dock/install-chart.tsx:113
|
||||
msgid "Helm Chart Install"
|
||||
msgstr "Helm Chart Install"
|
||||
@ -1627,6 +1651,10 @@ msgstr "Privileged"
|
||||
msgid "Provisioner"
|
||||
msgstr "Provisioner"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:48
|
||||
msgid "Proxy settings"
|
||||
msgstr "Proxy settings"
|
||||
|
||||
#: src/renderer/components/+workloads-pods/pods.tsx:79
|
||||
msgid "QoS"
|
||||
msgstr "QoS"
|
||||
@ -1987,6 +2015,10 @@ msgstr "Secrets"
|
||||
msgid "Select a quota.."
|
||||
msgstr "Select a quota.."
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:45
|
||||
msgid "Select kubeconfig"
|
||||
msgstr "Select kubeconfig"
|
||||
|
||||
#: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:188
|
||||
msgid "Select role.."
|
||||
msgstr "Select role.."
|
||||
|
||||
@ -41,6 +41,10 @@ msgstr ""
|
||||
msgid "<0>{0}</0> successfully created"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:52
|
||||
msgid "A HTTP proxy server URL (format: http://<address>:<port>)"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:80
|
||||
msgid "API Group"
|
||||
msgstr ""
|
||||
@ -63,6 +67,10 @@ msgstr ""
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:44
|
||||
msgid "Add Cluster"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+namespaces/namespaces.tsx:43
|
||||
msgid "Add Namespace"
|
||||
msgstr ""
|
||||
@ -75,6 +83,10 @@ msgstr ""
|
||||
msgid "Add bindings to {name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:62
|
||||
msgid "Add cluster"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+workspaces/clusters-menu.tsx:58
|
||||
#~ msgid "Add clusters"
|
||||
#~ msgstr ""
|
||||
@ -609,11 +621,19 @@ msgstr ""
|
||||
msgid "Currently applied filters:"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:36
|
||||
#~ msgid "Custom"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/renderer/components/+custom-resources/crd-list.tsx:55
|
||||
#: src/renderer/components/layout/sidebar.tsx:95
|
||||
msgid "Custom Resources"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:36
|
||||
msgid "Custom.."
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:95
|
||||
msgid "DNS Provider"
|
||||
msgstr ""
|
||||
@ -853,6 +873,10 @@ msgstr ""
|
||||
msgid "HPA"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:54
|
||||
msgid "HTTP Proxy server. Used for communicating with Kubernetes API."
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/dock/install-chart.tsx:113
|
||||
msgid "Helm Chart Install"
|
||||
msgstr ""
|
||||
@ -1610,6 +1634,10 @@ msgstr ""
|
||||
msgid "Provisioner"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:48
|
||||
msgid "Proxy settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+workloads-pods/pods.tsx:79
|
||||
msgid "QoS"
|
||||
msgstr ""
|
||||
@ -1970,6 +1998,10 @@ msgstr ""
|
||||
msgid "Select a quota.."
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:45
|
||||
msgid "Select kubeconfig"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:188
|
||||
msgid "Select role.."
|
||||
msgstr ""
|
||||
|
||||
@ -42,6 +42,10 @@ msgstr "<0>Ваш браузер не поддерживает все возмо
|
||||
msgid "<0>{0}</0> successfully created"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:52
|
||||
msgid "A HTTP proxy server URL (format: http://<address>:<port>)"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:80
|
||||
msgid "API Group"
|
||||
msgstr ""
|
||||
@ -64,6 +68,10 @@ msgstr "Название аккаунта"
|
||||
msgid "Active"
|
||||
msgstr "Активный"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:44
|
||||
msgid "Add Cluster"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+namespaces/namespaces.tsx:43
|
||||
msgid "Add Namespace"
|
||||
msgstr "Добавить Namespace"
|
||||
@ -76,6 +84,10 @@ msgstr "Добавить привязку ролей"
|
||||
msgid "Add bindings to {name}"
|
||||
msgstr "Добавить привязки к {name}"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:62
|
||||
msgid "Add cluster"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+workspaces/clusters-menu.tsx:58
|
||||
#~ msgid "Add clusters"
|
||||
#~ msgstr ""
|
||||
@ -614,11 +626,19 @@ msgstr "Текущий размер реплики: {currentReplicas}"
|
||||
msgid "Currently applied filters:"
|
||||
msgstr "Текущие фильтры:"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:36
|
||||
#~ msgid "Custom"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/renderer/components/+custom-resources/crd-list.tsx:55
|
||||
#: src/renderer/components/layout/sidebar.tsx:95
|
||||
msgid "Custom Resources"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:36
|
||||
msgid "Custom.."
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:95
|
||||
msgid "DNS Provider"
|
||||
msgstr "DNS провайдер"
|
||||
@ -863,6 +883,10 @@ msgstr "Группы"
|
||||
msgid "HPA"
|
||||
msgstr "HPA"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:54
|
||||
msgid "HTTP Proxy server. Used for communicating with Kubernetes API."
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/dock/install-chart.tsx:113
|
||||
msgid "Helm Chart Install"
|
||||
msgstr "Установка Helm чарта"
|
||||
@ -1628,6 +1652,10 @@ msgstr ""
|
||||
msgid "Provisioner"
|
||||
msgstr "Комиссия"
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:48
|
||||
msgid "Proxy settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+workloads-pods/pods.tsx:79
|
||||
msgid "QoS"
|
||||
msgstr "QoS"
|
||||
@ -1988,6 +2016,10 @@ msgstr "Secrets"
|
||||
msgid "Select a quota.."
|
||||
msgstr "Выберите квоту..."
|
||||
|
||||
#: src/renderer/components/+add-cluster/add-cluster.tsx:45
|
||||
msgid "Select kubeconfig"
|
||||
msgstr ""
|
||||
|
||||
#: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:188
|
||||
msgid "Select role.."
|
||||
msgstr "Выбрать роль.."
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { app, remote } from "electron";
|
||||
import { KubeConfig, V1Node, V1Pod } from "@kubernetes/client-node"
|
||||
import { ensureDirSync, writeFileSync } from "fs-extra";
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
import yaml from "js-yaml"
|
||||
import logger from "./logger";
|
||||
import logger from "../main/logger";
|
||||
|
||||
function resolveTilde(filePath: string) {
|
||||
if (filePath[0] === "~" && (filePath[1] === "/" || filePath.length === 1)) {
|
||||
@ -112,6 +114,7 @@ export function dumpConfigYaml(kubeConfig: Partial<KubeConfig>): string {
|
||||
}
|
||||
|
||||
logger.debug("Dumping KubeConfig:", config);
|
||||
|
||||
// skipInvalid: true makes dump ignore undefined values
|
||||
return yaml.safeDump(config, { skipInvalid: true });
|
||||
}
|
||||
@ -136,3 +139,13 @@ export function getNodeWarningConditions(node: V1Node) {
|
||||
c.status.toLowerCase() === "true" && c.type !== "Ready" && c.type !== "HostUpgrades"
|
||||
)
|
||||
}
|
||||
|
||||
// Write kubeconfigs to "embedded" store, i.e. "/Users/ixrock/Library/Application Support/Lens/kubeconfigs"
|
||||
export function saveConfigToAppFiles(clusterId: string, kubeConfig: string): string {
|
||||
const userData = (app || remote.app).getPath("userData");
|
||||
const kubeConfigFile = path.join(userData, `kubeconfigs/${clusterId}`)
|
||||
|
||||
ensureDirSync(path.dirname(kubeConfigFile));
|
||||
writeFileSync(kubeConfigFile, kubeConfig);
|
||||
return kubeConfigFile;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import { app, remote } from "electron"
|
||||
import { ensureDirSync, writeFileSync } from "fs-extra"
|
||||
import * as path from "path"
|
||||
|
||||
// Write kubeconfigs to "embedded" store, i.e. "/Users/ixrock/Library/Application Support/Lens/kubeconfigs"
|
||||
export function writeEmbeddedKubeConfig(clusterId: string, kubeConfig: string): string {
|
||||
const userData = (app || remote.app).getPath("userData");
|
||||
const kubeConfigBase = path.join(userData, "kubeconfigs")
|
||||
ensureDirSync(kubeConfigBase)
|
||||
|
||||
const kubeConfigFile = path.join(kubeConfigBase, clusterId)
|
||||
writeFileSync(kubeConfigFile, kubeConfig)
|
||||
|
||||
return kubeConfigFile
|
||||
}
|
||||
@ -9,7 +9,7 @@ import { ClusterId, ClusterModel, clusterStore } from "../common/cluster-store"
|
||||
import { handleMessages } from "../common/ipc";
|
||||
import { ClusterIpcMessage } from "../common/ipc-messages";
|
||||
import { tracker } from "../common/tracker";
|
||||
import { validateConfig } from "./k8s";
|
||||
import { validateConfig } from "../common/kube-helpers";
|
||||
import { Cluster } from "./cluster"
|
||||
import { FeatureInstallRequest } from "./feature";
|
||||
import logger from "./logger"
|
||||
|
||||
@ -8,7 +8,7 @@ import { ContextHandler } from "./context-handler"
|
||||
import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"
|
||||
import { Kubectl } from "./kubectl";
|
||||
import { KubeconfigManager } from "./kubeconfig-manager"
|
||||
import { getNodeWarningConditions, loadConfig, podHasIssues } from "./k8s"
|
||||
import { getNodeWarningConditions, loadConfig, podHasIssues } from "../common/kube-helpers"
|
||||
import { getFeatures, installFeature, uninstallFeature, upgradeFeature } from "./feature-manager";
|
||||
import request, { RequestPromiseOptions } from "request-promise-native"
|
||||
import logger from "./logger"
|
||||
|
||||
@ -4,7 +4,7 @@ import type { ContextHandler } from "./context-handler";
|
||||
import { app } from "electron"
|
||||
import path from "path"
|
||||
import fs from "fs-extra"
|
||||
import { dumpConfigYaml, loadConfig } from "./k8s"
|
||||
import { dumpConfigYaml, loadConfig } from "../common/kube-helpers"
|
||||
import logger from "./logger"
|
||||
|
||||
export class KubeconfigManager {
|
||||
|
||||
@ -4,13 +4,12 @@ import path from "path"
|
||||
import { app, remote } from "electron"
|
||||
import { migration } from "../migration-wrapper";
|
||||
import { ensureDirSync } from "fs-extra"
|
||||
import { writeEmbeddedKubeConfig } from "../../common/utils/kubeconfig"
|
||||
import { ClusterModel } from "../../common/cluster-store";
|
||||
import { loadConfig } from "../../main/k8s";
|
||||
import { loadConfig, saveConfigToAppFiles } from "../../common/kube-helpers";
|
||||
|
||||
export default migration({
|
||||
version: "3.6.0-beta.1",
|
||||
run(store, log: (...args: any[]) => void) {
|
||||
run(store, printLog) {
|
||||
const migratedClusters: ClusterModel[] = []
|
||||
const storedClusters: ClusterModel[] = store.get("clusters");
|
||||
const kubeConfigBase = path.join((app || remote.app).getPath("userData"), "kubeconfigs")
|
||||
@ -18,16 +17,16 @@ export default migration({
|
||||
if (!storedClusters) return;
|
||||
ensureDirSync(kubeConfigBase);
|
||||
|
||||
log("Number of clusters to migrate: ", storedClusters.length)
|
||||
printLog("Number of clusters to migrate: ", storedClusters.length)
|
||||
for (const cluster of storedClusters) {
|
||||
try {
|
||||
// take the embedded kubeconfig and dump it into a file
|
||||
cluster.kubeConfigPath = writeEmbeddedKubeConfig(cluster.id, cluster.kubeConfig)
|
||||
cluster.kubeConfigPath = saveConfigToAppFiles(cluster.id, cluster.kubeConfig)
|
||||
cluster.contextName = loadConfig(cluster.kubeConfigPath).getCurrentContext();
|
||||
delete cluster.kubeConfig;
|
||||
migratedClusters.push(cluster)
|
||||
} catch (error) {
|
||||
log(`Failed to migrate Kubeconfig for cluster "${cluster.id}"`, error)
|
||||
printLog(`Failed to migrate Kubeconfig for cluster "${cluster.id}"`, error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -75,46 +75,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
<b-col lg="5" class="help d-none d-lg-block">
|
||||
<h3>Clusters associated with Lens</h3>
|
||||
<p>
|
||||
Add clusters by clicking the <span class="text-primary">Add Cluster</span> button.
|
||||
You'll need to obtain a working kubeconfig for the cluster you want to add.
|
||||
</p>
|
||||
<p>
|
||||
Each <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context">cluster context</a> is added as a separate item in the left-side cluster menu to allow you to operate easily on multiple clusters and/or contexts.
|
||||
</p>
|
||||
<p>
|
||||
For more information on kubeconfig see <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/">Kubernetes docs</a>
|
||||
</p>
|
||||
<p>
|
||||
NOTE: Any manually added cluster is not merged into your kubeconfig file.
|
||||
</p>
|
||||
<p>
|
||||
To see your currently enabled config with <code>kubectl</code>, use <code>kubectl config view --minify --raw</code> command in your terminal.
|
||||
</p>
|
||||
<p>
|
||||
When connecting to a cluster, make sure you have a valid and working kubeconfig for the cluster. Following lists known "gotchas" in some authentication types used in kubeconfig with Lens app.
|
||||
</p>
|
||||
<a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#option-1-oidc-authenticator">
|
||||
<h4>OIDC (OpenID Connect)</h4>
|
||||
</a>
|
||||
<div>
|
||||
<p>
|
||||
When connecting Lens to OIDC enabled cluster, there's few things you as a user need to take into account.
|
||||
</p>
|
||||
<b>Dedicated refresh token</b>
|
||||
<p>
|
||||
As Lens app utilized kubeconfig is "disconnected" from your main kubeconfig Lens needs to have it's own refresh token it utilizes.
|
||||
If you share the refresh token with e.g. <code>kubectl</code> who ever uses the token first will invalidate it for the next user.
|
||||
One way to achieve this is with <a href="https://github.com/int128/kubelogin">kubelogin</a> tool by removing the tokens (both <code>id_token</code> and <code>refresh_token</code>) from the config and issuing <code>kubelogin</code> command. That'll take you through the login process and will result you having "dedicated" refresh token.
|
||||
</p>
|
||||
</div>
|
||||
<h4>Exec auth plugins</h4>
|
||||
<p>
|
||||
When using <a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuration">exec auth</a> plugins make sure the paths that are used to call any binaries are full paths as Lens app might not be able to call binaries with relative paths. Make also sure that you pass all needed information either as arguments or env variables in the config, Lens app might not have all login shell env variables set automatically.
|
||||
</p>
|
||||
</b-col>
|
||||
<!--info-panel-->
|
||||
</b-row>
|
||||
</b-container>
|
||||
</div>
|
||||
@ -123,12 +84,11 @@
|
||||
<script>
|
||||
import * as PrismEditor from 'vue-prism-editor'
|
||||
import * as k8s from "@kubernetes/client-node"
|
||||
import { dumpConfigYaml } from "../../../main/k8s"
|
||||
import { dumpConfigYaml } from "../../../common/kube-helpers"
|
||||
import ClustersMixin from "@/_vue/mixins/ClustersMixin";
|
||||
import * as path from "path"
|
||||
import fs from 'fs'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { writeEmbeddedKubeConfig} from "../../../common/utils/kubeconfig"
|
||||
|
||||
class ClusterAccessError extends Error {}
|
||||
|
||||
@ -154,8 +114,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
const kubeConfigPath = path.join(process.env.HOME, '.kube', 'config')
|
||||
this.filepath = kubeConfigPath
|
||||
this.filepath = path.join(process.env.HOME, '.kube', 'config')
|
||||
this.file = new File(fs.readFileSync(this.filepath), this.filepath)
|
||||
this.$store.dispatch("reloadAvailableKubeContexts", this.filepath);
|
||||
this.seenContexts = JSON.parse(JSON.stringify(this.$store.getters.seenContexts)) // clone seenContexts from store
|
||||
@ -206,12 +165,6 @@ export default {
|
||||
this.clusterconfig = this.kubecontext;
|
||||
}
|
||||
},
|
||||
isOidcAuth: function(authProvider) {
|
||||
if (!authProvider) { return false }
|
||||
if (authProvider.name === "oidc") { return true }
|
||||
|
||||
return false;
|
||||
},
|
||||
doAddCluster: async function() {
|
||||
// Clear previous error details
|
||||
this.errorMsg = ""
|
||||
@ -224,7 +177,7 @@ export default {
|
||||
const clusterId = uuidv4();
|
||||
// We need to store the kubeconfig to "app-home"/
|
||||
if (this.kubecontext === "custom") {
|
||||
this.filepath = writeEmbeddedKubeConfig(clusterId, this.clusterconfig)
|
||||
this.filepath = saveConfigToAppFiles(clusterId, this.clusterconfig)
|
||||
}
|
||||
const clusterInfo = {
|
||||
id: clusterId,
|
||||
|
||||
@ -1,3 +1,26 @@
|
||||
.AddCluster {
|
||||
|
||||
--flex-gap: #{$padding * 2};
|
||||
|
||||
position: relative;
|
||||
padding: $padding * 3;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 40%;
|
||||
|
||||
> .content {
|
||||
padding: var(--flex-gap);
|
||||
margin-right: var(--flex-gap);
|
||||
background-color: $contentColor;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
> .info-panel {
|
||||
@include hidden-scrollbar;
|
||||
padding: var(--flex-gap);
|
||||
border-left: 1px solid #353a3e;
|
||||
|
||||
a {
|
||||
color: $colorInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,137 @@
|
||||
import "./add-cluster.scss"
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
interface Props {
|
||||
}
|
||||
import { computed, observable } from "mobx";
|
||||
import path from "path";
|
||||
import { Select, SelectOption } from "../select";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { Input } from "../input";
|
||||
import { _i18n } from "../../i18n";
|
||||
import { AceEditor } from "../ace-editor";
|
||||
import { Button } from "../button";
|
||||
|
||||
@observer
|
||||
export class AddCluster extends React.Component<Props> {
|
||||
export class AddCluster extends React.Component {
|
||||
readonly customContext = "custom"
|
||||
readonly kubeConfigFile = path.join(process.env.HOME, '.kube', 'config');
|
||||
|
||||
@observable useProxySettings = false
|
||||
@observable clusterContext = ""
|
||||
@observable error = ""
|
||||
@observable proxyServerUrl = ""
|
||||
@observable customConfig = ""
|
||||
|
||||
// todo: mark new contexts with badge
|
||||
@computed get clusterOptions(): SelectOption[] {
|
||||
return [
|
||||
{
|
||||
label: <Trans>Custom..</Trans>,
|
||||
value: this.customContext,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// todo
|
||||
addCluster = () => {
|
||||
console.log('add new cluster')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="AddCluster">
|
||||
AddCluster
|
||||
<div className="content flex column gaps">
|
||||
<h2><Trans>Add Cluster</Trans></h2>
|
||||
<Select
|
||||
placeholder={<Trans>Select kubeconfig</Trans>}
|
||||
value={this.clusterContext}
|
||||
options={this.clusterOptions}
|
||||
onChange={({ value }: SelectOption) => this.clusterContext = value}
|
||||
/>
|
||||
<div className="use-proxy">
|
||||
<a onClick={() => this.useProxySettings = !this.useProxySettings}>
|
||||
<Trans>Proxy settings</Trans>
|
||||
</a>
|
||||
</div>
|
||||
{this.useProxySettings && (
|
||||
<div className="proxy-settings">
|
||||
<Input
|
||||
autoFocus
|
||||
placeholder={_i18n._(t`A HTTP proxy server URL (format: http://<address>:<port>)`)}
|
||||
value={this.proxyServerUrl}
|
||||
onChange={value => this.proxyServerUrl = value}
|
||||
/>
|
||||
<small className="hint">
|
||||
<Trans>HTTP Proxy server. Used for communicating with Kubernetes API.</Trans>
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
{this.clusterContext === this.customContext && (
|
||||
<div className="custom-kubeconfig flex column gaps box grow">
|
||||
<p>Kubeconfig:</p>
|
||||
<AceEditor
|
||||
autoFocus
|
||||
mode="yaml"
|
||||
value={this.customConfig}
|
||||
onChange={value => this.customConfig = value}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="actions-panel">
|
||||
<Button
|
||||
primary
|
||||
label={<Trans>Add cluster</Trans>}
|
||||
onClick={this.addCluster}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="info-panel flex column gaps">
|
||||
<h2>Clusters associated with Lens</h2>
|
||||
<p>
|
||||
Add clusters by clicking the <span className="text-primary">Add Cluster</span> button.
|
||||
You'll need to obtain a working kubeconfig for the cluster you want to add.
|
||||
</p>
|
||||
<p>
|
||||
Each <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context" target="_blank">cluster context</a> is added as a separate item in the
|
||||
left-side cluster menu
|
||||
to allow you to operate easily on multiple clusters and/or contexts.
|
||||
</p>
|
||||
<p>
|
||||
For more information on kubeconfig see <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/" target="_blank">Kubernetes docs</a>
|
||||
</p>
|
||||
<p>
|
||||
NOTE: Any manually added cluster is not merged into your kubeconfig file.
|
||||
</p>
|
||||
<p>
|
||||
To see your currently enabled config with <code>kubectl</code>, use <code>kubectl config view --minify --raw</code> command in your terminal.
|
||||
</p>
|
||||
<p>
|
||||
When connecting to a cluster, make sure you have a valid and working kubeconfig for the cluster. Following lists known "gotchas" in some authentication types used in kubeconfig with Lens
|
||||
app.
|
||||
</p>
|
||||
<a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#option-1-oidc-authenticator" target="_blank">
|
||||
<h4>OIDC (OpenID Connect)</h4>
|
||||
</a>
|
||||
<div>
|
||||
<p>
|
||||
When connecting Lens to OIDC enabled cluster, there's few things you as a user need to take into account.
|
||||
</p>
|
||||
<b>Dedicated refresh token</b>
|
||||
<p>
|
||||
As Lens app utilized kubeconfig is "disconnected" from your main kubeconfig Lens needs to have it's own refresh token it utilizes.
|
||||
If you share the refresh token with e.g. <code>kubectl</code> who ever uses the token first will invalidate it for the next user.
|
||||
One way to achieve this is with <a href="https://github.com/int128/kubelogin" target="_blank">kubelogin</a> tool by removing the tokens
|
||||
(both <code>id_token</code> and <code>refresh_token</code>) from
|
||||
the config and issuing <code>kubelogin</code> command. That'll take you through the login process and will result you having "dedicated" refresh token.
|
||||
</p>
|
||||
</div>
|
||||
<h4>Exec auth plugins</h4>
|
||||
<p>
|
||||
When using <a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuration" target="_blank">exec auth</a> plugins make sure the paths that are used to call
|
||||
any binaries
|
||||
are full paths as Lens app might not be able to call binaries with relative paths. Make also sure that you pass all needed information either as arguments or env variables in the config,
|
||||
Lens app might not have all login shell env variables set automatically.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -26,13 +26,6 @@
|
||||
line-height: 120%;
|
||||
padding-left: $spacing * 2;
|
||||
}
|
||||
|
||||
code {
|
||||
display: inline-block;
|
||||
padding: 0.25em .5em;
|
||||
vertical-align: middle;
|
||||
border-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
> .bottom {
|
||||
|
||||
@ -114,14 +114,21 @@ small {
|
||||
}
|
||||
|
||||
code {
|
||||
@include custom-scrollbar;
|
||||
display: inline-block;
|
||||
padding: 0.2em;
|
||||
vertical-align: middle;
|
||||
border-radius: $radius;
|
||||
font-family: $font-monospace;
|
||||
font-size: $font-size * .9;
|
||||
display: block;
|
||||
background: #1a1919;
|
||||
color: #b4b5b4;
|
||||
padding: $padding * 2;
|
||||
white-space: pre;
|
||||
|
||||
&.block {
|
||||
@include hidden-scrollbar;
|
||||
background: #1a1919;
|
||||
display: block;
|
||||
white-space: pre;
|
||||
padding: $padding * 2;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@ -41,7 +41,7 @@ export class LogsDialog extends React.Component<Props> {
|
||||
<Dialog {...dialogProps} className="LogsDialog">
|
||||
<Wizard header={header} done={dialogProps.close}>
|
||||
<WizardStep scrollable={false} customButtons={customButtons}>
|
||||
<code ref={e => this.logsElem = e}>
|
||||
<code className="block" ref={e => this.logsElem = e}>
|
||||
{logs || <Trans>There are no logs available.</Trans>}
|
||||
</code>
|
||||
</WizardStep>
|
||||
|
||||
@ -52,7 +52,7 @@ export class ErrorBoundary extends React.Component<Props, State> {
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gaps">
|
||||
<code>
|
||||
<code className="block">
|
||||
<p className="contrast"><Trans>Component stack</Trans>:</p>
|
||||
{errorInfo.componentStack}
|
||||
</code>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user