diff --git a/src/main/__test__/kubeconfig-manager.test.ts b/src/main/__test__/kubeconfig-manager.test.ts index 89c882b109..d04f7492f2 100644 --- a/src/main/__test__/kubeconfig-manager.test.ts +++ b/src/main/__test__/kubeconfig-manager.test.ts @@ -85,8 +85,8 @@ describe("kubeconfig manager tests", () => { const kubeConfManager = await KubeconfigManager.create(cluster, contextHandler, port); expect(logger.error).not.toBeCalled(); - expect(kubeConfManager.getPath()).toBe(`tmp${path.sep}kubeconfig-foo`); - const file = await fse.readFile(kubeConfManager.getPath()); + expect(await kubeConfManager.getPath()).toBe(`tmp${path.sep}kubeconfig-foo`); + const file = await fse.readFile(await kubeConfManager.getPath()); const yml = loadYaml(file.toString()); expect(yml["current-context"]).toBe("minikube"); @@ -104,12 +104,12 @@ describe("kubeconfig manager tests", () => { const contextHandler = new ContextHandler(cluster); const port = await getFreePort(); const kubeConfManager = await KubeconfigManager.create(cluster, contextHandler, port); - const configPath = kubeConfManager.getPath(); + const configPath = await kubeConfManager.getPath(); expect(await fse.pathExists(configPath)).toBe(true); await kubeConfManager.unlink(); expect(await fse.pathExists(configPath)).toBe(false); await kubeConfManager.unlink(); // doesn't throw - expect(kubeConfManager.getPath()).toBeUndefined(); + expect(await kubeConfManager.getPath()).toBeUndefined(); }); }); diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 8920568c73..169c99c5a8 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -482,14 +482,16 @@ export class Cluster implements ClusterModel, ClusterState { /** * @internal */ - getProxyKubeconfig(): KubeConfig { - return loadConfig(this.getProxyKubeconfigPath()); + async getProxyKubeconfig(): Promise { + const kubeconfigPath = await this.getProxyKubeconfigPath(); + + return loadConfig(kubeconfigPath); } /** * @internal */ - getProxyKubeconfigPath(): string { + async getProxyKubeconfigPath(): Promise { return this.kubeconfigManager.getPath(); } @@ -565,7 +567,7 @@ export class Cluster implements ClusterModel, ClusterState { * @param resourceAttributes resource attributes */ async canI(resourceAttributes: V1ResourceAttributes): Promise { - const authApi = this.getProxyKubeconfig().makeApiClient(AuthorizationV1Api); + const authApi = (await this.getProxyKubeconfig()).makeApiClient(AuthorizationV1Api); try { const accessReview = await authApi.createSelfSubjectAccessReview({ @@ -680,14 +682,14 @@ export class Cluster implements ClusterModel, ClusterState { return this.accessibleNamespaces; } - const api = this.getProxyKubeconfig().makeApiClient(CoreV1Api); + const api = (await this.getProxyKubeconfig()).makeApiClient(CoreV1Api); try { const namespaceList = await api.listNamespace(); return namespaceList.body.items.map(ns => ns.metadata.name); } catch (error) { - const ctx = this.getProxyKubeconfig().getContextObject(this.contextName); + const ctx = (await this.getProxyKubeconfig()).getContextObject(this.contextName); if (ctx.namespace) return [ctx.namespace]; diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index d67c495a84..e94520b9be 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -59,7 +59,7 @@ export class ContextHandler { async getPrometheusService(): Promise { const providers = this.prometheusProvider ? prometheusProviders.filter(provider => provider.id == this.prometheusProvider) : prometheusProviders; const prometheusPromises: Promise[] = providers.map(async (provider: PrometheusProvider): Promise => { - const apiClient = this.cluster.getProxyKubeconfig().makeApiClient(CoreV1Api); + const apiClient = (await this.cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api); return await provider.getPrometheusService(apiClient); }); diff --git a/src/main/helm/helm-release-manager.ts b/src/main/helm/helm-release-manager.ts index 220d665ae0..58a4e99798 100644 --- a/src/main/helm/helm-release-manager.ts +++ b/src/main/helm/helm-release-manager.ts @@ -56,11 +56,12 @@ export class HelmReleaseManager { public async upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, cluster: Cluster){ const helm = await helmCli.binaryPath(); const fileName = tempy.file({name: "values.yaml"}); + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); await fs.promises.writeFile(fileName, yaml.safeDump(values)); try { - const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr);}); + const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`).catch((error) => { throw(error.stderr);}); return { log: stdout, @@ -73,7 +74,9 @@ export class HelmReleaseManager { public async getRelease(name: string, namespace: string, cluster: Cluster) { const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${cluster.getProxyKubeconfigPath()}`).catch((error) => { throw(error.stderr);}); + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + + const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`).catch((error) => { throw(error.stderr);}); const release = JSON.parse(stdout); release.resources = await this.getResources(name, namespace, cluster); @@ -112,7 +115,7 @@ export class HelmReleaseManager { protected async getResources(name: string, namespace: string, cluster: Cluster) { const helm = await helmCli.binaryPath(); const kubectl = await cluster.kubeCtl.getPath(); - const pathToKubeconfig = cluster.getProxyKubeconfigPath(); + const pathToKubeconfig = await cluster.getProxyKubeconfigPath(); const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`).catch(() => { return { stdout: JSON.stringify({items: []})}; }); diff --git a/src/main/helm/helm-service.ts b/src/main/helm/helm-service.ts index f7445cebd4..9682e58a84 100644 --- a/src/main/helm/helm-service.ts +++ b/src/main/helm/helm-service.ts @@ -8,7 +8,9 @@ import { HelmChartList, RepoHelmChartList } from "../../renderer/api/endpoints/h class HelmService { public async installChart(cluster: Cluster, data: { chart: string; values: {}; name: string; namespace: string; version: string }) { - return await releaseManager.installChart(data.chart, data.values, data.name, data.namespace, data.version, cluster.getProxyKubeconfigPath()); + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + + return await releaseManager.installChart(data.chart, data.values, data.name, data.namespace, data.version, proxyKubeconfig); } public async listCharts() { @@ -53,8 +55,9 @@ class HelmService { public async listReleases(cluster: Cluster, namespace: string = null) { await repoManager.init(); + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); - return await releaseManager.listReleases(cluster.getProxyKubeconfigPath(), namespace); + return await releaseManager.listReleases(proxyKubeconfig, namespace); } public async getRelease(cluster: Cluster, releaseName: string, namespace: string) { @@ -64,21 +67,27 @@ class HelmService { } public async getReleaseValues(cluster: Cluster, releaseName: string, namespace: string) { + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + logger.debug("Fetch release values"); - return await releaseManager.getValues(releaseName, namespace, cluster.getProxyKubeconfigPath()); + return await releaseManager.getValues(releaseName, namespace, proxyKubeconfig); } public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) { + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + logger.debug("Fetch release history"); - return await releaseManager.getHistory(releaseName, namespace, cluster.getProxyKubeconfigPath()); + return await releaseManager.getHistory(releaseName, namespace, proxyKubeconfig); } public async deleteRelease(cluster: Cluster, releaseName: string, namespace: string) { + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + logger.debug("Delete release"); - return await releaseManager.deleteRelease(releaseName, namespace, cluster.getProxyKubeconfigPath()); + return await releaseManager.deleteRelease(releaseName, namespace, proxyKubeconfig); } public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) { @@ -88,8 +97,10 @@ class HelmService { } public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) { + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + logger.debug("Rollback release"); - const output = await releaseManager.rollback(releaseName, namespace, revision, cluster.getProxyKubeconfigPath()); + const output = await releaseManager.rollback(releaseName, namespace, revision, proxyKubeconfig); return { message: output }; } diff --git a/src/main/kubeconfig-manager.ts b/src/main/kubeconfig-manager.ts index bd1b32c1f5..f88ce87840 100644 --- a/src/main/kubeconfig-manager.ts +++ b/src/main/kubeconfig-manager.ts @@ -24,14 +24,24 @@ export class KubeconfigManager { protected async init() { try { await this.contextHandler.ensurePort(); - await this.createProxyKubeconfig(); + this.tempFile = await this.createProxyKubeconfig(); } catch (err) { logger.error(`Failed to created temp config for auth-proxy`, { err }); } } - getPath() { + async getPath() { + // create proxy kubeconfig if it is removed + if (this.tempFile !== undefined && !(await fs.pathExists(this.tempFile))) { + try { + this.tempFile = await this.createProxyKubeconfig(); + } catch (err) { + logger.error(`Failed to created temp config for auth-proxy`, { err }); + } + } + return this.tempFile; + } protected resolveProxyUrl() { @@ -71,9 +81,8 @@ export class KubeconfigManager { // write const configYaml = dumpConfigYaml(proxyConfig); - fs.ensureDir(path.dirname(tempFile)); - fs.writeFileSync(tempFile, configYaml, { mode: 0o600 }); - this.tempFile = tempFile; + await fs.ensureDir(path.dirname(tempFile)); + await fs.writeFile(tempFile, configYaml, { mode: 0o600 }); logger.debug(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${configYaml}`); return tempFile; diff --git a/src/main/node-shell-session.ts b/src/main/node-shell-session.ts index b67e776725..0799f9f892 100644 --- a/src/main/node-shell-session.ts +++ b/src/main/node-shell-session.ts @@ -17,10 +17,10 @@ export class NodeShellSession extends ShellSession { super(socket, cluster); this.nodeName = nodeName; this.podId = `node-shell-${uuid()}`; - this.kc = cluster.getProxyKubeconfig(); } public async open() { + this.kc = await this.cluster.getProxyKubeconfig(); const shell = await this.kubectl.getPath(); let args = []; diff --git a/src/main/resource-applier.ts b/src/main/resource-applier.ts index d4070f2378..6f1b0a8e0f 100644 --- a/src/main/resource-applier.ts +++ b/src/main/resource-applier.ts @@ -23,12 +23,13 @@ export class ResourceApplier { protected async kubectlApply(content: string): Promise { const { kubeCtl } = this.cluster; const kubectlPath = await kubeCtl.getPath(); + const proxyKubeconfigPath = await this.cluster.getProxyKubeconfigPath(); return new Promise((resolve, reject) => { const fileName = tempy.file({ name: "resource.yaml" }); fs.writeFileSync(fileName, content); - const cmd = `"${kubectlPath}" apply --kubeconfig "${this.cluster.getProxyKubeconfigPath()}" -o json -f "${fileName}"`; + const cmd = `"${kubectlPath}" apply --kubeconfig "${proxyKubeconfigPath}" -o json -f "${fileName}"`; logger.debug(`shooting manifests with: ${cmd}`); const execEnv: NodeJS.ProcessEnv = Object.assign({}, process.env); @@ -54,6 +55,7 @@ export class ResourceApplier { public async kubectlApplyAll(resources: string[]): Promise { const { kubeCtl } = this.cluster; const kubectlPath = await kubeCtl.getPath(); + const proxyKubeconfigPath = await this.cluster.getProxyKubeconfigPath(); return new Promise((resolve, reject) => { const tmpDir = tempy.directory(); @@ -62,7 +64,7 @@ export class ResourceApplier { resources.forEach((resource, index) => { fs.writeFileSync(path.join(tmpDir, `${index}.yaml`), resource); }); - const cmd = `"${kubectlPath}" apply --kubeconfig "${this.cluster.getProxyKubeconfigPath()}" -o json -f "${tmpDir}"`; + const cmd = `"${kubectlPath}" apply --kubeconfig "${proxyKubeconfigPath}" -o json -f "${tmpDir}"`; console.log("shooting manifests with:", cmd); exec(cmd, (error, stdout, stderr) => { diff --git a/src/main/routes/kubeconfig-route.ts b/src/main/routes/kubeconfig-route.ts index 7fe5bcb9bc..bad5ecd57a 100644 --- a/src/main/routes/kubeconfig-route.ts +++ b/src/main/routes/kubeconfig-route.ts @@ -44,7 +44,7 @@ class KubeconfigRoute extends LensApi { public async routeServiceAccountRoute(request: LensApiRequest) { const { params, response, cluster} = request; - const client = cluster.getProxyKubeconfig().makeApiClient(CoreV1Api); + const client = (await cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api); const secretList = await client.listNamespacedSecret(params.namespace); const secret = secretList.body.items.find(secret => { const { annotations } = secret.metadata; diff --git a/src/main/routes/port-forward-route.ts b/src/main/routes/port-forward-route.ts index 33c34758a4..0b5954948d 100644 --- a/src/main/routes/port-forward-route.ts +++ b/src/main/routes/port-forward-route.ts @@ -92,7 +92,7 @@ class PortForwardRoute extends LensApi { namespace, name: resourceName, port, - kubeConfig: cluster.getProxyKubeconfigPath() + kubeConfig: await cluster.getProxyKubeconfigPath() }); const started = await portForward.start(); diff --git a/src/main/shell-session.ts b/src/main/shell-session.ts index 40b3981d07..1582ede43e 100644 --- a/src/main/shell-session.ts +++ b/src/main/shell-session.ts @@ -6,7 +6,6 @@ import shellEnv from "shell-env"; import { app } from "electron"; import { Kubectl } from "./kubectl"; import { Cluster } from "./cluster"; -import { ClusterPreferences } from "../common/cluster-store"; import { helmCli } from "./helm/helm-cli"; import { isWindows } from "../common/vars"; import { appEventBus } from "../common/event-bus"; @@ -24,20 +23,18 @@ export class ShellSession extends EventEmitter { protected kubectlBinDir: string; protected kubectlPathDir: string; protected helmBinDir: string; - protected preferences: ClusterPreferences; protected running = false; - protected clusterId: string; + protected cluster: Cluster; constructor(socket: WebSocket, cluster: Cluster) { super(); this.websocket = socket; - this.kubeconfigPath = cluster.getProxyKubeconfigPath(); this.kubectl = new Kubectl(cluster.version); - this.preferences = cluster.preferences || {}; - this.clusterId = cluster.id; + this.cluster = cluster; } public async open() { + this.kubeconfigPath = await this.cluster.getProxyKubeconfigPath(); this.kubectlBinDir = await this.kubectl.binDir(); const pathFromPreferences = userStore.preferences.kubectlBinariesPath || this.kubectl.getBundledPath(); @@ -65,11 +62,13 @@ export class ShellSession extends EventEmitter { } protected cwd(): string { - if(!this.preferences || !this.preferences.terminalCWD || this.preferences.terminalCWD === "") { + const { preferences } = this.cluster; + + if(!preferences || !preferences.terminalCWD || preferences.terminalCWD === "") { return null; } - return this.preferences.terminalCWD; + return preferences.terminalCWD; } protected async getShellArgs(shell: string): Promise> { @@ -88,15 +87,17 @@ export class ShellSession extends EventEmitter { } protected async getCachedShellEnv() { - let env = ShellSession.shellEnvs.get(this.clusterId); + const { id: clusterId } = this.cluster; + + let env = ShellSession.shellEnvs.get(clusterId); if (!env) { env = await this.getShellEnv(); - ShellSession.shellEnvs.set(this.clusterId, env); + ShellSession.shellEnvs.set(clusterId, env); } else { // refresh env in the background this.getShellEnv().then((shellEnv: any) => { - ShellSession.shellEnvs.set(this.clusterId, shellEnv); + ShellSession.shellEnvs.set(clusterId, shellEnv); }); } @@ -107,6 +108,7 @@ export class ShellSession extends EventEmitter { const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(await shellEnv()))); const pathStr = [this.kubectlBinDir, this.helmBinDir, process.env.PATH].join(path.delimiter); const shell = userStore.preferences.shell || process.env.SHELL || process.env.PTYSHELL; + const { preferences } = this.cluster; if(isWindows) { env["SystemRoot"] = process.env.SystemRoot; @@ -138,8 +140,8 @@ export class ShellSession extends EventEmitter { env["TERM_PROGRAM"] = app.getName(); env["TERM_PROGRAM_VERSION"] = app.getVersion(); - if (this.preferences.httpsProxy) { - env["HTTPS_PROXY"] = this.preferences.httpsProxy; + if (preferences.httpsProxy) { + env["HTTPS_PROXY"] = preferences.httpsProxy; } const no_proxy = ["localhost", "127.0.0.1", env["NO_PROXY"]];