import "./add-cluster.scss" import path from "path"; import fs from "fs-extra"; import React, { Fragment } from "react"; import { observer } from "mobx-react"; import { computed, observable } from "mobx"; 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"; import { KubeConfig } from "@kubernetes/client-node"; import { loadConfig, saveConfigToAppFiles, splitConfig, validateConfig } from "../../../common/kube-helpers"; import { tracker } from "../../../common/tracker"; import { clusterStore } from "../../../common/cluster-store"; import { workspaceStore } from "../../../common/workspace-store"; import { v4 as uuid } from "uuid" import { navigation } from "../../navigation"; import { WizardLayout } from "../layout/wizard-layout"; @observer export class AddCluster extends React.Component { readonly custom: any = "custom" @observable.ref clusterConfig: KubeConfig; @observable.ref kubeConfig: KubeConfig; // local ~/.kube/config (if available) @observable.ref error: React.ReactNode; @observable isWaiting = false @observable showSettings = false @observable proxyServer = "" @observable customConfig = "" async componentDidMount() { const kubeConfig = await this.readLocalKubeConfig(); if (kubeConfig) { this.kubeConfig = loadConfig(kubeConfig) this.customConfig = kubeConfig } } async readLocalKubeConfig(): Promise { const localPath = path.join(process.env.HOME, '.kube', 'config'); return fs.readFile(localPath, "utf8").catch(() => null) } @computed get isCustom() { return this.clusterConfig === this.custom; } @computed get clusterOptions() { const options: SelectOption[] = []; if (this.kubeConfig) { const contexts = splitConfig(this.kubeConfig) .filter(kc => !clusterStore.hasContext(kc.currentContext)); contexts.forEach(kubeConfig => { const isNew = false; // fixme: detect new context since last visit options.push({ value: kubeConfig, label: <> {kubeConfig.currentContext} {isNew && (new)} , }) }) } options.push({ label: Custom.., value: this.custom, }); return options; } addCluster = async () => { tracker.event("cluster", "add"); const { clusterConfig, customConfig, proxyServer } = this; const clusterId = uuid(); this.isWaiting = true this.error = "" try { const config = this.isCustom ? loadConfig(customConfig) : clusterConfig; if (!config) { this.error = Please select kubeconfig return; } validateConfig(config); await clusterStore.addCluster({ id: clusterId, kubeConfigPath: saveConfigToAppFiles(clusterId, config), workspace: workspaceStore.currentWorkspaceId, contextName: config.currentContext, preferences: { clusterName: config.currentContext, httpsProxy: proxyServer || undefined, }, }); navigation.goBack(); // return to previous opened page for the cluster view } catch (err) { this.error = String(err); } finally { this.isWaiting = false; } } renderInfo() { return (

Clusters associated with Lens

Add clusters by clicking the Add Cluster button. You'll need to obtain a working kubeconfig for the cluster you want to add.

Each cluster context is added as a separate item in the left-side cluster menu to allow you to operate easily on multiple clusters and/or contexts.

For more information on kubeconfig see Kubernetes docs

NOTE: Any manually added cluster is not merged into your kubeconfig file.

To see your currently enabled config with kubectl, use kubectl config view --minify --raw command in your terminal.

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.

OIDC (OpenID Connect)

When connecting Lens to OIDC enabled cluster, there's few things you as a user need to take into account.

Dedicated refresh token

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. kubectl who ever uses the token first will invalidate it for the next user. One way to achieve this is with kubelogin tool by removing the tokens (both id_token and refresh_token) from the config and issuing kubelogin command. That'll take you through the login process and will result you having "dedicated" refresh token.

Exec auth plugins

When using exec auth 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.

) } render() { return (

Add Cluster

:)`)} value={this.proxyServer} onChange={value => this.proxyServer = value} /> HTTP Proxy server. Used for communicating with Kubernetes API. )} {this.isCustom && (

Kubeconfig:

this.customConfig = value} />
)} {this.error && (
{this.error}
)}
) } }