1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix downloading cluster specific kubectl (#5413)

This commit is contained in:
Sebastian Malton 2022-05-18 13:20:14 -07:00 committed by GitHub
parent 30a9409faf
commit 710671e2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 270 additions and 67 deletions

View File

@ -24,6 +24,9 @@ import directoryForUserDataInjectable from "../app-paths/directory-for-user-data
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable";
import normalizedPlatformInjectable from "../vars/normalized-platform.injectable";
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
@ -86,6 +89,9 @@ describe("cluster-store", () => {
mainDi.override(clusterStoreInjectable, (di) => ClusterStore.createInstance({ createCluster: di.inject(createClusterInjectionToken) })); mainDi.override(clusterStoreInjectable, (di) => ClusterStore.createInstance({ createCluster: di.inject(createClusterInjectionToken) }));
mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
mainDi.override(kubectlBinaryNameInjectable, () => "kubectl");
mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
mainDi.override(normalizedPlatformInjectable, () => "darwin");
mainDi.permitSideEffects(getConfigurationFileModelInjectable); mainDi.permitSideEffects(getConfigurationFileModelInjectable);
mainDi.permitSideEffects(appVersionInjectable); mainDi.permitSideEffects(appVersionInjectable);

View File

@ -1,13 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { baseBinariesDir } from "../../vars";
const directoryForBundledBinariesInjectable = getInjectable({
id: "directory-for-bundled-binaries",
instantiate: () => baseBinariesDir.get(),
});
export default directoryForBundledBinariesInjectable;

View File

@ -29,6 +29,9 @@ export const defaultTheme = "lens-dark" as string;
export const defaultFontSize = 12; export const defaultFontSize = 12;
export const defaultTerminalFontFamily = "RobotoMono"; export const defaultTerminalFontFamily = "RobotoMono";
export const defaultEditorFontFamily = "RobotoMono"; export const defaultEditorFontFamily = "RobotoMono";
/**
* @deprecated use `di.inject(normalizedPlatformInjectable)` instead
*/
export const normalizedPlatform = (() => { export const normalizedPlatform = (() => {
switch (process.platform) { switch (process.platform) {
case "darwin": case "darwin":
@ -41,6 +44,9 @@ export const normalizedPlatform = (() => {
throw new Error(`platform=${process.platform} is unsupported`); throw new Error(`platform=${process.platform} is unsupported`);
} }
})(); })();
/**
* @deprecated use `di.inject(bundledBinariesNormalizedArchInjectable)` instead
*/
export const normalizedArch = (() => { export const normalizedArch = (() => {
switch (process.arch) { switch (process.arch) {
case "arm64": case "arm64":
@ -91,15 +97,6 @@ export const helmBinaryName = getBinaryName("helm");
*/ */
export const helmBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), helmBinaryName)); export const helmBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), helmBinaryName));
/**
* @deprecated for being explicit side effect.
*/
export const kubectlBinaryName = getBinaryName("kubectl");
/**
* @deprecated for being explicit side effect.
*/
export const kubectlBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), kubectlBinaryName));
export const staticFilesDirectory = path.resolve( export const staticFilesDirectory = path.resolve(
!isProduction !isProduction
? process.cwd() ? process.cwd()

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import path from "path";
import bundledBinariesNormalizedArchInjectable from "./bundled-binaries-normalized-arch.injectable";
import bundledResourcesDirectoryInjectable from "./bundled-resources-dir.injectable";
const baseBundeledBinariesDirectoryInjectable = getInjectable({
id: "base-bundeled-binaries-directory",
instantiate: (di) => path.join(
di.inject(bundledResourcesDirectoryInjectable),
di.inject(bundledBinariesNormalizedArchInjectable),
),
});
export default baseBundeledBinariesDirectoryInjectable;

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
const bundledBinariesNormalizedArchInjectable = getInjectable({
id: "bundled-binaries-normalized-arch",
instantiate: () => {
switch (process.arch) {
case "arm64":
return "arm64";
case "x64":
case "amd64":
return "x64";
case "386":
case "x32":
case "ia32":
return "ia32";
default:
throw new Error(`arch=${process.arch} is unsupported`);
}
},
causesSideEffects: true,
});
export default bundledBinariesNormalizedArchInjectable;

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import path from "path";
import isProductionInjectable from "./is-production.injectable";
import normalizedPlatformInjectable from "./normalized-platform.injectable";
const bundledResourcesDirectoryInjectable = getInjectable({
id: "bundled-resources-directory",
instantiate: (di) => {
const isProduction = di.inject(isProductionInjectable);
const normalizedPlatform = di.inject(normalizedPlatformInjectable);
return isProduction
? process.resourcesPath
: path.join(process.cwd(), "binaries", "client", normalizedPlatform);
},
});
export default bundledResourcesDirectoryInjectable;

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
const isProductionInjectable = getInjectable({
id: "is-production",
instantiate: () => process.env.NODE_ENV === "production",
causesSideEffects: true,
});
export default isProductionInjectable;

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
const normalizedPlatformInjectable = getInjectable({
id: "normalized-platform",
instantiate: () => {
switch (process.platform) {
case "darwin":
return "darwin";
case "linux":
return "linux";
case "win32":
return "windows";
default:
throw new Error(`platform=${process.platform} is unsupported`);
}
},
causesSideEffects: true,
});
export default normalizedPlatformInjectable;

View File

@ -43,6 +43,9 @@ import { createClusterInjectionToken } from "../../common/cluster/create-cluster
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable"; import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable"; import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable"; import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
console = new Console(process.stdout, process.stderr); // fix mockFS console = new Console(process.stdout, process.stderr); // fix mockFS
@ -81,6 +84,10 @@ describe("create clusters", () => {
await di.runSetups(); await di.runSetups();
di.override(kubectlBinaryNameInjectable, () => "kubectl");
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true)); di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true));
di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ])); di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
di.override(createContextHandlerInjectable, () => () => { di.override(createContextHandlerInjectable, () => () => {

View File

@ -57,6 +57,9 @@ import path from "path";
import spawnInjectable from "../child-process/spawn.injectable"; import spawnInjectable from "../child-process/spawn.injectable";
import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable"; import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
@ -100,6 +103,9 @@ describe("kube auth proxy tests", () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true }); const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(spawnInjectable, () => mockSpawn); di.override(spawnInjectable, () => mockSpawn);
di.override(kubectlBinaryNameInjectable, () => "kubectl");
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable); di.permitSideEffects(appVersionInjectable);

View File

@ -43,18 +43,24 @@ import { createClusterInjectionToken } from "../../common/cluster/create-cluster
import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable"; import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable"; import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
import type { DiContainer } from "@ogre-tools/injectable"; import type { DiContainer } from "@ogre-tools/injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
console = new Console(process.stdout, process.stderr); // fix mockFS console = new Console(process.stdout, process.stderr); // fix mockFS
describe("kubeconfig manager tests", () => { describe("kubeconfig manager tests", () => {
let clusterFake: Cluster; let clusterFake: Cluster;
let createKubeconfigManager: (cluster: Cluster) => KubeconfigManager; let createKubeconfigManager: (cluster: Cluster) => KubeconfigManager;
let di: DiContainer; let di: DiContainer;
beforeEach(async () => { beforeEach(async () => {
di = getDiForUnitTesting({ doGeneralOverrides: true }); di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForTempInjectable, () => "some-directory-for-temp"); di.override(directoryForTempInjectable, () => "some-directory-for-temp");
di.override(kubectlBinaryNameInjectable, () => "kubectl");
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
mockFs({ mockFs({
"minikube-config.yml": JSON.stringify({ "minikube-config.yml": JSON.stringify({

View File

@ -16,10 +16,11 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token"; import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import { ClusterStore } from "../../../common/cluster-store/cluster-store"; import { ClusterStore } from "../../../common/cluster-store/cluster-store";
import getConfigurationFileModelInjectable import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
import appVersionInjectable import kubectlBinaryNameInjectable from "../../kubectl/binary-name.injectable";
from "../../../common/get-configuration-file-model/app-version/app-version.injectable"; import kubectlDownloadingNormalizedArchInjectable from "../../kubectl/normalized-arch.injectable";
import normalizedPlatformInjectable from "../../../common/vars/normalized-platform.injectable";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -47,6 +48,10 @@ describe("kubeconfig-sync.source tests", () => {
await di.runSetups(); await di.runSetups();
di.override(kubectlBinaryNameInjectable, () => "kubectl");
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
computeDiff = computeDiffFor({ computeDiff = computeDiffFor({
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable), directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
createCluster: di.inject(createClusterInjectionToken), createCluster: di.inject(createClusterInjectionToken),

View File

@ -4,9 +4,8 @@
*/ */
import glob from "glob"; import glob from "glob";
import { kebabCase, memoize, noop } from "lodash/fp"; import { kebabCase, memoize } from "lodash/fp";
import { createContainer } from "@ogre-tools/injectable"; import { createContainer } from "@ogre-tools/injectable";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import getElectronAppPathInjectable from "./app-paths/get-electron-app-path/get-electron-app-path.injectable"; import getElectronAppPathInjectable from "./app-paths/get-electron-app-path/get-electron-app-path.injectable";
import setElectronAppPathInjectable from "./app-paths/set-electron-app-path/set-electron-app-path.injectable"; import setElectronAppPathInjectable from "./app-paths/set-electron-app-path/set-electron-app-path.injectable";
@ -15,7 +14,6 @@ import registerChannelInjectable from "./app-paths/register-channel/register-cha
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable"; import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
import readJsonFileInjectable from "../common/fs/read-json-file.injectable"; import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
import readFileInjectable from "../common/fs/read-file.injectable"; import readFileInjectable from "../common/fs/read-file.injectable";
import directoryForBundledBinariesInjectable from "../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
import loggerInjectable from "../common/logger.injectable"; import loggerInjectable from "../common/logger.injectable";
import spawnInjectable from "./child-process/spawn.injectable"; import spawnInjectable from "./child-process/spawn.injectable";
import extensionsStoreInjectable from "../extensions/extensions-store/extensions-store.injectable"; import extensionsStoreInjectable from "../extensions/extensions-store/extensions-store.injectable";
@ -35,6 +33,9 @@ import { getAbsolutePathFake } from "../common/test-utils/get-absolute-path-fake
import joinPathsInjectable from "../common/path/join-paths.injectable"; import joinPathsInjectable from "../common/path/join-paths.injectable";
import { joinPathsFake } from "../common/test-utils/join-paths-fake"; import { joinPathsFake } from "../common/test-utils/join-paths-fake";
import hotbarStoreInjectable from "../common/hotbar-store.injectable"; import hotbarStoreInjectable from "../common/hotbar-store.injectable";
import { noop } from "../common/utils";
import baseBundeledBinariesDirectoryInjectable from "../common/vars/base-bundled-binaries-dir.injectable";
export const getDiForUnitTesting = ( export const getDiForUnitTesting = (
{ doGeneralOverrides } = { doGeneralOverrides: false }, { doGeneralOverrides } = { doGeneralOverrides: false },
@ -82,8 +83,7 @@ export const getDiForUnitTesting = (
di.override(setElectronAppPathInjectable, () => () => undefined); di.override(setElectronAppPathInjectable, () => () => undefined);
di.override(appNameInjectable, () => "some-electron-app-name"); di.override(appNameInjectable, () => "some-electron-app-name");
di.override(registerChannelInjectable, () => () => undefined); di.override(registerChannelInjectable, () => () => undefined);
di.override(directoryForBundledBinariesInjectable, () => "some-bin-directory"); di.override(baseBundeledBinariesDirectoryInjectable, () => "some-bin-directory");
di.override(spawnInjectable, () => () => { di.override(spawnInjectable, () => () => {
return { return {
stderr: { on: jest.fn(), removeAllListeners: jest.fn() }, stderr: { on: jest.fn(), removeAllListeners: jest.fn() },

View File

@ -9,9 +9,9 @@ import type { Cluster } from "../../common/cluster/cluster";
import path from "path"; import path from "path";
import selfsigned from "selfsigned"; import selfsigned from "selfsigned";
import { getBinaryName } from "../../common/vars"; import { getBinaryName } from "../../common/vars";
import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
import spawnInjectable from "../child-process/spawn.injectable"; import spawnInjectable from "../child-process/spawn.injectable";
import { getKubeAuthProxyCertificate } from "./get-kube-auth-proxy-certificate"; import { getKubeAuthProxyCertificate } from "./get-kube-auth-proxy-certificate";
import baseBundeledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy; export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy;
@ -24,7 +24,7 @@ const createKubeAuthProxyInjectable = getInjectable({
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => { return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => {
const clusterUrl = new URL(cluster.apiUrl); const clusterUrl = new URL(cluster.apiUrl);
const dependencies: KubeAuthProxyDependencies = { const dependencies: KubeAuthProxyDependencies = {
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName), proxyBinPath: path.join(di.inject(baseBundeledBinariesDirectoryInjectable), binaryName),
proxyCert: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate), proxyCert: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate),
spawn: di.inject(spawnInjectable), spawn: di.inject(spawnInjectable),
}; };

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
const kubectlBinaryNameInjectable = getInjectable({
id: "kubectl-binary-name",
instantiate: (di) => {
const platform = di.inject(normalizedPlatformInjectable);
return platform === "windows"
? "kubectl.exe"
: "kubectl";
},
});
export default kubectlBinaryNameInjectable;

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import path from "path";
import baseBundeledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
import kubectlBinaryNameInjectable from "./binary-name.injectable";
const bundledKubectlBinaryPathInjectable = getInjectable({
id: "bundled-kubectl-binary-path",
instantiate: (di) => path.join(
di.inject(baseBundeledBinariesDirectoryInjectable),
di.inject(kubectlBinaryNameInjectable),
),
});
export default bundledKubectlBinaryPathInjectable;

View File

@ -3,24 +3,31 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { KubectlDependencies } from "./kubectl";
import { Kubectl } from "./kubectl"; import { Kubectl } from "./kubectl";
import directoryForKubectlBinariesInjectable from "../../common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable"; import directoryForKubectlBinariesInjectable from "../../common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable";
import userStoreInjectable from "../../common/user-store/user-store.injectable"; import userStoreInjectable from "../../common/user-store/user-store.injectable";
import kubectlDownloadingNormalizedArchInjectable from "./normalized-arch.injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "./binary-name.injectable";
import bundledKubectlBinaryPathInjectable from "./bundled-binary-path.injectable";
import baseBundeledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
const createKubectlInjectable = getInjectable({ const createKubectlInjectable = getInjectable({
id: "create-kubectl", id: "create-kubectl",
instantiate: (di) => { instantiate: (di) => {
const dependencies = { const dependencies: KubectlDependencies = {
userStore: di.inject(userStoreInjectable), userStore: di.inject(userStoreInjectable),
directoryForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable),
directoryForKubectlBinaries: di.inject( normalizedDownloadArch: di.inject(kubectlDownloadingNormalizedArchInjectable),
directoryForKubectlBinariesInjectable, normalizedDownloadPlatform: di.inject(normalizedPlatformInjectable),
), kubectlBinaryName: di.inject(kubectlBinaryNameInjectable),
bundledKubectlBinaryPath: di.inject(bundledKubectlBinaryPathInjectable),
baseBundeledBinariesDirectory: di.inject(baseBundeledBinariesDirectoryInjectable),
}; };
return (clusterVersion: string) => return (clusterVersion: string) => new Kubectl(dependencies, clusterVersion);
new Kubectl(dependencies, clusterVersion);
}, },
}); });

View File

@ -10,7 +10,6 @@ import logger from "../logger";
import { ensureDir, pathExists } from "fs-extra"; import { ensureDir, pathExists } from "fs-extra";
import * as lockFile from "proper-lockfile"; import * as lockFile from "proper-lockfile";
import { getBundledKubectlVersion } from "../../common/utils/app-version"; import { getBundledKubectlVersion } from "../../common/utils/app-version";
import { normalizedPlatform, normalizedArch, kubectlBinaryName, kubectlBinaryPath, baseBinariesDir } from "../../common/vars";
import { SemVer } from "semver"; import { SemVer } from "semver";
import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers"; import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers";
import got from "got/dist/source"; import got from "got/dist/source";
@ -40,28 +39,31 @@ const kubectlMap: Map<string, string> = new Map([
]); ]);
const initScriptVersionString = "# lens-initscript v3"; const initScriptVersionString = "# lens-initscript v3";
interface Dependencies { export interface KubectlDependencies {
directoryForKubectlBinaries: string; readonly directoryForKubectlBinaries: string;
readonly normalizedDownloadPlatform: "darwin" | "linux" | "windows";
userStore: { readonly normalizedDownloadArch: "amd64" | "arm64" | "386";
kubectlBinariesPath?: string; readonly kubectlBinaryName: string;
downloadBinariesPath?: string; readonly bundledKubectlBinaryPath: string;
downloadKubectlBinaries: boolean; readonly baseBundeledBinariesDirectory: string;
downloadMirror: string; readonly userStore: {
readonly kubectlBinariesPath?: string;
readonly downloadBinariesPath?: string;
readonly downloadKubectlBinaries: boolean;
readonly downloadMirror: string;
}; };
} }
export class Kubectl { export class Kubectl {
public kubectlVersion: string; public readonly kubectlVersion: string;
protected directory: string; protected readonly url: string;
protected url: string; protected readonly path: string;
protected path: string; protected readonly dirname: string;
protected dirname: string;
public static readonly bundledKubectlVersion: string = bundledVersion; public static readonly bundledKubectlVersion: string = bundledVersion;
public static invalidBundle = false; public static invalidBundle = false;
constructor(private dependencies: Dependencies, clusterVersion: string) { constructor(protected readonly dependencies: KubectlDependencies, clusterVersion: string) {
let version: SemVer; let version: SemVer;
try { try {
@ -82,13 +84,13 @@ export class Kubectl {
logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`); logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`);
} }
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${normalizedPlatform}/${normalizedArch}/${kubectlBinaryName}`; this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${this.dependencies.normalizedDownloadPlatform}/${this.dependencies.normalizedDownloadArch}/${this.dependencies.kubectlBinaryName}`;
this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion)); this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion));
this.path = path.join(this.dirname, kubectlBinaryName); this.path = path.join(this.dirname, this.dependencies.kubectlBinaryName);
} }
public getBundledPath() { public getBundledPath() {
return kubectlBinaryPath.get(); return this.dependencies.bundledKubectlBinaryPath;
} }
public getPathFromPreferences() { public getPathFromPreferences() {
@ -278,12 +280,11 @@ export class Kubectl {
} }
protected async writeInitScripts() { protected async writeInitScripts() {
const binariesDir = this.dependencies.baseBundeledBinariesDirectory;
const kubectlPath = this.dependencies.userStore.downloadKubectlBinaries const kubectlPath = this.dependencies.userStore.downloadKubectlBinaries
? this.dirname ? this.dirname
: path.dirname(this.getPathFromPreferences()); : path.dirname(this.getPathFromPreferences());
const binariesDir = baseBinariesDir.get();
const bashScriptPath = path.join(this.dirname, ".bash_set_path"); const bashScriptPath = path.join(this.dirname, ".bash_set_path");
const bashScript = [ const bashScript = [
initScriptVersionString, initScriptVersionString,
@ -296,7 +297,7 @@ export class Kubectl {
"elif test -f \"$HOME/.profile\"; then", "elif test -f \"$HOME/.profile\"; then",
" . \"$HOME/.profile\"", " . \"$HOME/.profile\"",
"fi", "fi",
`export PATH="${binariesDir}:${kubectlPath}:$PATH"`, `export PATH="${kubectlPath}:${binariesDir}:$PATH"`,
'export KUBECONFIG="$tempkubeconfig"', 'export KUBECONFIG="$tempkubeconfig"',
`NO_PROXY=",\${NO_PROXY:-localhost},"`, `NO_PROXY=",\${NO_PROXY:-localhost},"`,
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`, `NO_PROXY="\${NO_PROXY//,localhost,/,}"`,
@ -327,7 +328,7 @@ export class Kubectl {
"d=\":$PATH:\"", "d=\":$PATH:\"",
`d=\${d//$p/:}`, `d=\${d//$p/:}`,
`d=\${d/#:/}`, `d=\${d/#:/}`,
`export PATH="$binariesDir:$kubectlpath:\${d/%:/}"`, `export PATH="$kubectlpath:$binariesDir:\${d/%:/}"`,
"export KUBECONFIG=\"$tempkubeconfig\"", "export KUBECONFIG=\"$tempkubeconfig\"",
`NO_PROXY=",\${NO_PROXY:-localhost},"`, `NO_PROXY=",\${NO_PROXY:-localhost},"`,
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`, `NO_PROXY="\${NO_PROXY//,localhost,/,}"`,

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
const kubectlDownloadingNormalizedArchInjectable = getInjectable({
id: "kubectl-downloading-normalized-arch",
instantiate: () => {
switch (process.arch) {
case "arm64":
return "arm64";
case "x64":
case "amd64":
return "amd64";
case "386":
case "x32":
case "ia32":
return "386";
default:
throw new Error(`arch=${process.arch} is unsupported`);
}
},
causesSideEffects: true,
});
export default kubectlDownloadingNormalizedArchInjectable;

View File

@ -14,6 +14,9 @@ import asyncFn from "@async-fn/jest";
import parseRequestInjectable from "./parse-request.injectable"; import parseRequestInjectable from "./parse-request.injectable";
import { contentTypes } from "./router-content-types"; import { contentTypes } from "./router-content-types";
import mockFs from "mock-fs"; import mockFs from "mock-fs";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
describe("router", () => { describe("router", () => {
let router: Router; let router: Router;
@ -27,6 +30,9 @@ describe("router", () => {
mockFs(); mockFs();
di.override(parseRequestInjectable, () => () => Promise.resolve({ payload: "some-payload" })); di.override(parseRequestInjectable, () => () => Promise.resolve({ payload: "some-payload" }));
di.override(kubectlBinaryNameInjectable, () => "kubectl");
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
await di.runSetups(); await di.runSetups();

View File

@ -45,11 +45,11 @@ export class LocalShellSession extends ShellSession {
switch(path.basename(shell)) { switch(path.basename(shell)) {
case "powershell.exe": case "powershell.exe":
return ["-NoExit", "-command", `& {$Env:PATH="${baseBinariesDir.get()};${kubectlPathDir};$Env:PATH"}`]; return ["-NoExit", "-command", `& {$Env:PATH="${kubectlPathDir};${baseBinariesDir.get()};$Env:PATH"}`];
case "bash": case "bash":
return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")]; return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")];
case "fish": case "fish":
return ["--login", "--init-command", `export PATH="${baseBinariesDir.get()}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`]; return ["--login", "--init-command", `export PATH="${kubectlPathDir}:${baseBinariesDir.get()}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`];
case "zsh": case "zsh":
return ["--login"]; return ["--login"];
default: default:

View File

@ -313,7 +313,7 @@ export abstract class ShellSession {
protected async getShellEnv() { protected async getShellEnv() {
const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(await shellEnv()))); const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(await shellEnv())));
const pathStr = [...this.getPathEntries(), await this.kubectlBinDirP, process.env.PATH].join(path.delimiter); const pathStr = [await this.kubectlBinDirP, ...this.getPathEntries(), process.env.PATH].join(path.delimiter);
const shell = UserStore.getInstance().resolvedShell; const shell = UserStore.getInstance().resolvedShell;
delete env.DEBUG; // don't pass DEBUG into shells delete env.DEBUG; // don't pass DEBUG into shells

View File

@ -21,6 +21,10 @@ import type { DeleteClusterDialogModel } from "../delete-cluster-dialog-model/de
import type { DiRender } from "../../test-utils/renderFor"; import type { DiRender } from "../../test-utils/renderFor";
import { renderFor } from "../../test-utils/renderFor"; import { renderFor } from "../../test-utils/renderFor";
import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable"; import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable";
import createKubeconfigManagerInjectable from "../../../../main/kubeconfig-manager/create-kubeconfig-manager.injectable";
import normalizedPlatformInjectable from "../../../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../../../../main/kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../../../../main/kubectl/normalized-arch.injectable";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
@ -102,7 +106,10 @@ describe("<DeleteClusterDialog />", () => {
render = renderFor(rendererDi); render = renderFor(rendererDi);
mainDi.override(createContextHandlerInjectable, () => () => undefined); mainDi.override(createContextHandlerInjectable, () => () => undefined);
mainDi.override(createKubeconfigManagerInjectable, () => () => undefined);
mainDi.override(kubectlBinaryNameInjectable, () => "kubectl");
mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
mainDi.override(normalizedPlatformInjectable, () => "darwin");
mockFs(); mockFs();
rendererDi.override(hotbarStoreInjectable, () => ({})); rendererDi.override(hotbarStoreInjectable, () => ({}));