mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Consolidate downloading binaries into one script (#4942)
* Consolidate downloading binaries into one script - Upgrade lens-k8s-proxy to 0.1.5 which allows us to remove the trailing slash in KubeAuthProxy - Consolidate nromalizing arch and os strings - Remove LensBinary as HelmCli is not just a raw object since it is always bundled - Introduce OnceCell and use it for the binary paths Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully remove helmCli, move associated variable to vars Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix helm downloading on windows Signed-off-by: Sebastian Malton <sebastian@malton.name> * Don't download binaries for unsupported windows Signed-off-by: Sebastian Malton <sebastian@malton.name> * Arch specific paths Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix downloading helm on windows Signed-off-by: Sebastian Malton <sebastian@malton.name> * rename once-cell file to lazy-initialized Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
dd5dfb393d
commit
a19f094a0d
23 files changed
+472
-641
No files matched your search
+2
-10
@@ -3,19 +3,11 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import path from "path";
|
||||
import isDevelopmentInjectable from "../../vars/is-development.injectable";
|
||||
import contextDirInjectable from "../../vars/context-dir.injectable";
|
||||
import { baseBinariesDir } from "../../vars";
|
||||
|
||||
const directoryForBundledBinariesInjectable = getInjectable({
|
||||
id: "directory-for-bundled-binaries",
|
||||
instantiate: (di) => {
|
||||
if (di.inject(isDevelopmentInjectable)) {
|
||||
return path.join(di.inject(contextDirInjectable), "binaries");
|
||||
}
|
||||
|
||||
return process.resourcesPath;
|
||||
},
|
||||
instantiate: () => baseBinariesDir.get(),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A OnceCell is an object that wraps some function that produces a value.
|
||||
*
|
||||
* It then only calls the function on the first call to `get()` and returns the
|
||||
* same instance/value on every subsequent call.
|
||||
*/
|
||||
export interface LazyInitialized<T> {
|
||||
get(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to make a `OnceCell<T>`
|
||||
*/
|
||||
export function lazyInitialized<T>(builder: () => T): LazyInitialized<T> {
|
||||
let value: T | undefined;
|
||||
let called = false;
|
||||
|
||||
return {
|
||||
get() {
|
||||
if (called) {
|
||||
return value;
|
||||
}
|
||||
|
||||
value = builder();
|
||||
called = true;
|
||||
|
||||
return value;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import path from "path";
|
||||
import { SemVer } from "semver";
|
||||
import packageInfo from "../../package.json";
|
||||
import { defineGlobal } from "./utils/defineGlobal";
|
||||
import { lazyInitialized } from "./utils/lazy-initialized";
|
||||
|
||||
export const isMac = process.platform === "darwin";
|
||||
export const isWindows = process.platform === "win32";
|
||||
@@ -29,6 +30,54 @@ export const defaultTheme = "lens-dark" as string;
|
||||
export const defaultFontSize = 12;
|
||||
export const defaultTerminalFontFamily = "RobotoMono";
|
||||
export const defaultEditorFontFamily = "RobotoMono";
|
||||
export const normalizedPlatform = (() => {
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return "darwin";
|
||||
case "linux":
|
||||
return "linux";
|
||||
case "win32":
|
||||
return "windows";
|
||||
default:
|
||||
throw new Error(`platform=${process.platform} is unsupported`);
|
||||
}
|
||||
})();
|
||||
export const normalizedArch = (() => {
|
||||
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`);
|
||||
}
|
||||
})();
|
||||
|
||||
export function getBinaryName(name: string, { forPlatform = normalizedPlatform } = {}): string {
|
||||
if (forPlatform === "windows") {
|
||||
return `${name}.exe`;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
const resourcesDir = lazyInitialized(() => (
|
||||
isProduction
|
||||
? process.resourcesPath
|
||||
: path.join(process.cwd(), "binaries", "client", normalizedPlatform)
|
||||
));
|
||||
|
||||
export const baseBinariesDir = lazyInitialized(() => path.join(resourcesDir.get(), normalizedArch));
|
||||
export const kubeAuthProxyBinaryName = getBinaryName("lens-k8s-proxy");
|
||||
export const helmBinaryName = getBinaryName("helm");
|
||||
export const helmBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), helmBinaryName));
|
||||
export const kubectlBinaryName = getBinaryName("kubectl");
|
||||
export const kubectlBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), kubectlBinaryName));
|
||||
|
||||
// Webpack build paths
|
||||
export const contextDir = process.cwd();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { promiseExecFile } from "../../common/utils/promise-exec";
|
||||
import type { BaseEncodingOptions } from "fs";
|
||||
import type { ExecFileOptions } from "child_process";
|
||||
import { helmBinaryPath } from "../../common/vars";
|
||||
|
||||
/**
|
||||
* ExecFile the bundled helm CLI
|
||||
* @returns STDOUT
|
||||
*/
|
||||
export async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
|
||||
try {
|
||||
const { stdout } = await promiseExecFile(helmBinaryPath.get(), args, options);
|
||||
|
||||
return stdout;
|
||||
} catch (error) {
|
||||
throw error?.stderr || error;
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,9 @@ import v8 from "v8";
|
||||
import * as yaml from "js-yaml";
|
||||
import type { HelmRepo } from "./helm-repo-manager";
|
||||
import logger from "../logger";
|
||||
import { promiseExecFile } from "../../common/utils/promise-exec";
|
||||
import { helmCli } from "./helm-cli";
|
||||
import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api";
|
||||
import { iter, sortCharts } from "../../common/utils";
|
||||
import { execHelm } from "./exec";
|
||||
|
||||
interface ChartCacheEntry {
|
||||
data: Buffer;
|
||||
@@ -49,21 +48,13 @@ export class HelmChartManager {
|
||||
}
|
||||
|
||||
private async executeCommand(args: string[], name: string, version?: string) {
|
||||
const helm = await helmCli.binaryPath();
|
||||
|
||||
args.push(`${this.repo.name}/${name}`);
|
||||
|
||||
if (version) {
|
||||
args.push("--version", version);
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await promiseExecFile(helm, args);
|
||||
|
||||
return stdout;
|
||||
} catch (error) {
|
||||
throw error.stderr || error;
|
||||
}
|
||||
return execHelm(args);
|
||||
}
|
||||
|
||||
public async getReadme(name: string, version?: string) {
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import packageInfo from "../../../package.json";
|
||||
import path from "path";
|
||||
import { LensBinary, LensBinaryOpts } from "../lens-binary";
|
||||
import { isProduction } from "../../common/vars";
|
||||
|
||||
export class HelmCli extends LensBinary {
|
||||
|
||||
public constructor(baseDir: string, version: string) {
|
||||
const opts: LensBinaryOpts = {
|
||||
version,
|
||||
baseDir,
|
||||
originalBinaryName: "helm",
|
||||
newBinaryName: "helm3",
|
||||
};
|
||||
|
||||
super(opts);
|
||||
}
|
||||
|
||||
protected getTarName(): string | null {
|
||||
return `${this.binaryName}-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`;
|
||||
}
|
||||
|
||||
protected getUrl() {
|
||||
return `https://get.helm.sh/helm-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`;
|
||||
}
|
||||
|
||||
protected getBinaryPath() {
|
||||
return path.join(this.dirname, this.binaryName);
|
||||
}
|
||||
|
||||
protected getOriginalBinaryPath() {
|
||||
return path.join(this.dirname, `${this.platformName}-${this.arch}`, this.originalBinaryName);
|
||||
}
|
||||
}
|
||||
|
||||
const helmVersion = packageInfo.config.bundledHelmVersion;
|
||||
let baseDir = process.resourcesPath;
|
||||
|
||||
if (!isProduction) {
|
||||
baseDir = path.join(process.cwd(), "binaries", "client", process.arch);
|
||||
}
|
||||
|
||||
export const helmCli = new HelmCli(baseDir, helmVersion);
|
||||
|
||||
@@ -6,23 +6,9 @@
|
||||
import tempy from "tempy";
|
||||
import fse from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import { promiseExecFile } from "../../common/utils/promise-exec";
|
||||
import { helmCli } from "./helm-cli";
|
||||
import { toCamelCase } from "../../common/utils/camelCase";
|
||||
import type { BaseEncodingOptions } from "fs";
|
||||
import { execFile, ExecFileOptions } from "child_process";
|
||||
|
||||
async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
|
||||
const helmCliPath = await helmCli.binaryPath();
|
||||
|
||||
try {
|
||||
const { stdout } = await promiseExecFile(helmCliPath, args, options);
|
||||
|
||||
return stdout;
|
||||
} catch (error) {
|
||||
throw error?.stderr || error;
|
||||
}
|
||||
}
|
||||
import { execFile } from "child_process";
|
||||
import { execHelm } from "./exec";
|
||||
|
||||
export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise<Record<string, any>[]> {
|
||||
const args = [
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
*/
|
||||
|
||||
import yaml from "js-yaml";
|
||||
import { BaseEncodingOptions, readFile } from "fs-extra";
|
||||
import { promiseExecFile } from "../../common/utils/promise-exec";
|
||||
import { helmCli } from "./helm-cli";
|
||||
import { readFile } from "fs-extra";
|
||||
import { Singleton } from "../../common/utils/singleton";
|
||||
import { customRequestPromise } from "../../common/request";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import logger from "../logger";
|
||||
import type { ExecFileOptions } from "child_process";
|
||||
import { execHelm } from "./exec";
|
||||
|
||||
export type HelmEnv = Record<string, string> & {
|
||||
HELM_REPOSITORY_CACHE?: string;
|
||||
@@ -34,18 +32,6 @@ export interface HelmRepo {
|
||||
password?: string;
|
||||
}
|
||||
|
||||
async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
|
||||
const helmCliPath = await helmCli.binaryPath();
|
||||
|
||||
try {
|
||||
const { stdout } = await promiseExecFile(helmCliPath, args, options);
|
||||
|
||||
return stdout;
|
||||
} catch (error) {
|
||||
throw error?.stderr || error;
|
||||
}
|
||||
}
|
||||
|
||||
export class HelmRepoManager extends Singleton {
|
||||
protected repos: HelmRepo[];
|
||||
protected helmEnv: HelmEnv;
|
||||
@@ -63,9 +49,6 @@ export class HelmRepoManager extends Singleton {
|
||||
}
|
||||
|
||||
private async ensureInitialized() {
|
||||
helmCli.setLogger(logger);
|
||||
await helmCli.ensureBinary();
|
||||
|
||||
this.helmEnv ??= await this.parseHelmEnv();
|
||||
|
||||
const repos = await this.list();
|
||||
|
||||
@@ -3,24 +3,24 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { KubeAuthProxy } from "./kube-auth-proxy";
|
||||
import { KubeAuthProxy, KubeAuthProxyDependencies } from "./kube-auth-proxy";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import path from "path";
|
||||
import { isDevelopment, isWindows } from "../../common/vars";
|
||||
import { getBinaryName } from "../../common/vars";
|
||||
import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
|
||||
|
||||
const createKubeAuthProxyInjectable = getInjectable({
|
||||
id: "create-kube-auth-proxy",
|
||||
|
||||
instantiate: (di) => {
|
||||
const binaryName = isWindows ? "lens-k8s-proxy.exe" : "lens-k8s-proxy";
|
||||
const proxyPath = isDevelopment ? path.join("client", process.platform, process.arch) : process.arch;
|
||||
const dependencies = {
|
||||
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), proxyPath, binaryName),
|
||||
const binaryName = getBinaryName("lens-k8s-proxy");
|
||||
const dependencies: KubeAuthProxyDependencies = {
|
||||
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName),
|
||||
};
|
||||
|
||||
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) =>
|
||||
new KubeAuthProxy(dependencies, cluster, environmentVariables);
|
||||
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => (
|
||||
new KubeAuthProxy(dependencies, cluster, environmentVariables)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { makeObservable, observable, when } from "mobx";
|
||||
|
||||
const startingServeRegex = /starting to serve on (?<address>.+)/i;
|
||||
|
||||
interface Dependencies {
|
||||
export interface KubeAuthProxyDependencies {
|
||||
proxyBinPath: string;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class KubeAuthProxy {
|
||||
protected proxyProcess?: ChildProcess;
|
||||
@observable protected ready = false;
|
||||
|
||||
constructor(private dependencies: Dependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) {
|
||||
constructor(private dependencies: KubeAuthProxyDependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ export class KubeAuthProxy {
|
||||
}
|
||||
|
||||
const proxyBin = this.dependencies.proxyBinPath;
|
||||
|
||||
this.proxyProcess = spawn(proxyBin, [], {
|
||||
|
||||
this.proxyProcess = spawn(proxyBin, [], {
|
||||
env: {
|
||||
...this.env,
|
||||
KUBECONFIG: this.cluster.kubeConfigPath,
|
||||
|
||||
@@ -9,9 +9,8 @@ import { promiseExecFile } from "../../common/utils/promise-exec";
|
||||
import logger from "../logger";
|
||||
import { ensureDir, pathExists } from "fs-extra";
|
||||
import * as lockFile from "proper-lockfile";
|
||||
import { helmCli } from "../helm/helm-cli";
|
||||
import { getBundledKubectlVersion } from "../../common/utils/app-version";
|
||||
import { isDevelopment, isWindows, isTestEnv } from "../../common/vars";
|
||||
import { normalizedPlatform, normalizedArch, kubectlBinaryName, kubectlBinaryPath, baseBinariesDir } from "../../common/vars";
|
||||
import { SemVer } from "semver";
|
||||
import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers";
|
||||
import got from "got/dist/source";
|
||||
@@ -39,27 +38,8 @@ const kubectlMap: Map<string, string> = new Map([
|
||||
["1.22", "1.22.6"],
|
||||
["1.23", bundledVersion],
|
||||
]);
|
||||
let bundledPath: string;
|
||||
const initScriptVersionString = "# lens-initscript v3";
|
||||
|
||||
export function bundledKubectlPath(): string {
|
||||
if (bundledPath) { return bundledPath; }
|
||||
|
||||
if (isDevelopment || isTestEnv) {
|
||||
const platformName = isWindows ? "windows" : process.platform;
|
||||
|
||||
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl");
|
||||
} else {
|
||||
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl");
|
||||
}
|
||||
|
||||
if (isWindows) {
|
||||
bundledPath = `${bundledPath}.exe`;
|
||||
}
|
||||
|
||||
return bundledPath;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
directoryForKubectlBinaries: string;
|
||||
|
||||
@@ -102,27 +82,13 @@ export class Kubectl {
|
||||
logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`);
|
||||
}
|
||||
|
||||
let arch = null;
|
||||
|
||||
if (process.arch == "x64") {
|
||||
arch = "amd64";
|
||||
} else if (process.arch == "x86" || process.arch == "ia32") {
|
||||
arch = "386";
|
||||
} else {
|
||||
arch = process.arch;
|
||||
}
|
||||
|
||||
const platformName = isWindows ? "windows" : process.platform;
|
||||
const binaryName = isWindows ? "kubectl.exe" : "kubectl";
|
||||
|
||||
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`;
|
||||
|
||||
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${normalizedPlatform}/${normalizedArch}/${kubectlBinaryName}`;
|
||||
this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion));
|
||||
this.path = path.join(this.dirname, binaryName);
|
||||
this.path = path.join(this.dirname, kubectlBinaryName);
|
||||
}
|
||||
|
||||
public getBundledPath() {
|
||||
return bundledKubectlPath();
|
||||
return kubectlBinaryPath.get();
|
||||
}
|
||||
|
||||
public getPathFromPreferences() {
|
||||
@@ -316,7 +282,7 @@ export class Kubectl {
|
||||
? this.dirname
|
||||
: path.dirname(this.getPathFromPreferences());
|
||||
|
||||
const helmPath = helmCli.getBinaryDir();
|
||||
const binariesDir = baseBinariesDir.get();
|
||||
|
||||
const bashScriptPath = path.join(this.dirname, ".bash_set_path");
|
||||
const bashScript = [
|
||||
@@ -330,7 +296,7 @@ export class Kubectl {
|
||||
"elif test -f \"$HOME/.profile\"; then",
|
||||
" . \"$HOME/.profile\"",
|
||||
"fi",
|
||||
`export PATH="${helmPath}:${kubectlPath}:$PATH"`,
|
||||
`export PATH="${binariesDir}:${kubectlPath}:$PATH"`,
|
||||
'export KUBECONFIG="$tempkubeconfig"',
|
||||
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
|
||||
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`,
|
||||
@@ -356,12 +322,12 @@ export class Kubectl {
|
||||
|
||||
// voodoo to replace any previous occurrences of kubectl path in the PATH
|
||||
`kubectlpath="${kubectlPath}"`,
|
||||
`helmpath="${helmPath}"`,
|
||||
`binariesDir="${binariesDir}"`,
|
||||
"p=\":$kubectlpath:\"",
|
||||
"d=\":$PATH:\"",
|
||||
`d=\${d//$p/:}`,
|
||||
`d=\${d/#:/}`,
|
||||
`export PATH="$helmpath:$kubectlpath:\${d/%:/}"`,
|
||||
`export PATH="$binariesDir:$kubectlpath:\${d/%:/}"`,
|
||||
"export KUBECONFIG=\"$tempkubeconfig\"",
|
||||
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
|
||||
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`,
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import request from "request";
|
||||
import { ensureDir, pathExists } from "fs-extra";
|
||||
import * as tar from "tar";
|
||||
import { isWindows } from "../common/vars";
|
||||
import type winston from "winston";
|
||||
|
||||
export interface LensBinaryOpts {
|
||||
version: string;
|
||||
baseDir: string;
|
||||
originalBinaryName: string;
|
||||
newBinaryName?: string;
|
||||
requestOpts?: request.Options;
|
||||
}
|
||||
|
||||
export class LensBinary {
|
||||
|
||||
public binaryVersion: string;
|
||||
protected directory: string;
|
||||
protected url: string;
|
||||
protected path: string;
|
||||
protected tarPath: string;
|
||||
protected dirname: string;
|
||||
protected binaryName: string;
|
||||
protected platformName: string;
|
||||
protected arch: string;
|
||||
protected originalBinaryName: string;
|
||||
protected requestOpts: request.Options;
|
||||
protected logger: Console | winston.Logger;
|
||||
|
||||
constructor(opts: LensBinaryOpts) {
|
||||
const baseDir = opts.baseDir;
|
||||
|
||||
this.originalBinaryName = opts.originalBinaryName;
|
||||
this.binaryName = opts.newBinaryName || opts.originalBinaryName;
|
||||
this.binaryVersion = opts.version;
|
||||
this.requestOpts = opts.requestOpts;
|
||||
this.logger = console;
|
||||
let arch = null;
|
||||
|
||||
if (process.env.BINARY_ARCH) {
|
||||
arch = process.env.BINARY_ARCH;
|
||||
} else if (process.arch == "x64") {
|
||||
arch = "amd64";
|
||||
} else if (process.arch == "x86" || process.arch == "ia32") {
|
||||
arch = "386";
|
||||
} else {
|
||||
arch = process.arch;
|
||||
}
|
||||
this.arch = arch;
|
||||
this.platformName = isWindows ? "windows" : process.platform;
|
||||
this.dirname = path.normalize(path.join(baseDir, this.binaryName));
|
||||
|
||||
if (isWindows) {
|
||||
this.binaryName = `${this.binaryName}.exe`;
|
||||
this.originalBinaryName = `${this.originalBinaryName}.exe`;
|
||||
}
|
||||
const tarName = this.getTarName();
|
||||
|
||||
if (tarName) {
|
||||
this.tarPath = path.join(this.dirname, tarName);
|
||||
}
|
||||
}
|
||||
|
||||
public setLogger(logger: Console | winston.Logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
protected binaryDir() {
|
||||
throw new Error("binaryDir not implemented");
|
||||
}
|
||||
|
||||
public async binaryPath() {
|
||||
await this.ensureBinary();
|
||||
|
||||
return this.getBinaryPath();
|
||||
}
|
||||
|
||||
protected getTarName(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected getUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected getBinaryPath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected getOriginalBinaryPath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public getBinaryDir() {
|
||||
return path.dirname(this.getBinaryPath());
|
||||
}
|
||||
|
||||
public async binDir() {
|
||||
try {
|
||||
await this.ensureBinary();
|
||||
|
||||
return this.dirname;
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected async checkBinary() {
|
||||
const exists = await pathExists(this.getBinaryPath());
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
public async ensureBinary() {
|
||||
const isValid = await this.checkBinary();
|
||||
|
||||
if (!isValid) {
|
||||
await this.downloadBinary().catch((error) => {
|
||||
this.logger.error(error);
|
||||
});
|
||||
if (this.tarPath) await this.untarBinary();
|
||||
if (this.originalBinaryName != this.binaryName) await this.renameBinary();
|
||||
this.logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`);
|
||||
}
|
||||
}
|
||||
|
||||
protected async untarBinary() {
|
||||
return new Promise<void>(resolve => {
|
||||
this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
|
||||
tar.x({
|
||||
file: this.tarPath,
|
||||
cwd: this.dirname,
|
||||
}).then((() => {
|
||||
resolve();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
protected async renameBinary() {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`);
|
||||
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected async downloadBinary() {
|
||||
const binaryPath = this.tarPath || this.getBinaryPath();
|
||||
|
||||
await ensureDir(this.getBinaryDir(), 0o755);
|
||||
|
||||
const file = fs.createWriteStream(binaryPath);
|
||||
const url = this.getUrl();
|
||||
|
||||
this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`);
|
||||
const requestOpts: request.UriOptions & request.CoreOptions = {
|
||||
uri: url,
|
||||
gzip: true,
|
||||
...this.requestOpts,
|
||||
};
|
||||
const stream = request(requestOpts);
|
||||
|
||||
stream.on("complete", () => {
|
||||
this.logger.info(`Download of ${this.originalBinaryName} finished`);
|
||||
file.end();
|
||||
});
|
||||
|
||||
stream.on("error", (error) => {
|
||||
this.logger.error(error);
|
||||
fs.unlink(binaryPath, () => {
|
||||
// do nothing
|
||||
});
|
||||
throw(error);
|
||||
});
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
file.on("close", () => {
|
||||
this.logger.debug(`${this.originalBinaryName} binary download closed`);
|
||||
if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => {
|
||||
if (err) reject(err);
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
stream.pipe(file);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
import type WebSocket from "ws";
|
||||
import path from "path";
|
||||
import { helmCli } from "../../helm/helm-cli";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { ClusterId } from "../../../common/cluster-types";
|
||||
import { ShellSession } from "../shell-session";
|
||||
import type { Kubectl } from "../../kubectl/kubectl";
|
||||
import { baseBinariesDir } from "../../../common/vars";
|
||||
|
||||
export class LocalShellSession extends ShellSession {
|
||||
ShellType = "shell";
|
||||
@@ -18,9 +18,9 @@ export class LocalShellSession extends ShellSession {
|
||||
constructor(protected shellEnvModify: (clusterId: ClusterId, env: Record<string, string>) => Record<string, string>, kubectl: Kubectl, websocket: WebSocket, cluster: Cluster, terminalId: string) {
|
||||
super(kubectl, websocket, cluster, terminalId);
|
||||
}
|
||||
|
||||
|
||||
protected getPathEntries(): string[] {
|
||||
return [helmCli.getBinaryDir()];
|
||||
return [baseBinariesDir.get()];
|
||||
}
|
||||
|
||||
protected get cwd(): string | undefined {
|
||||
@@ -40,17 +40,16 @@ export class LocalShellSession extends ShellSession {
|
||||
}
|
||||
|
||||
protected async getShellArgs(shell: string): Promise<string[]> {
|
||||
const helmpath = helmCli.getBinaryDir();
|
||||
const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath();
|
||||
const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
|
||||
|
||||
switch(path.basename(shell)) {
|
||||
case "powershell.exe":
|
||||
return ["-NoExit", "-command", `& {$Env:PATH="${helmpath};${kubectlPathDir};$Env:PATH"}`];
|
||||
return ["-NoExit", "-command", `& {$Env:PATH="${baseBinariesDir.get()};${kubectlPathDir};$Env:PATH"}`];
|
||||
case "bash":
|
||||
return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")];
|
||||
case "fish":
|
||||
return ["--login", "--init-command", `export PATH="${helmpath}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`];
|
||||
return ["--login", "--init-command", `export PATH="${baseBinariesDir.get()}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`];
|
||||
case "zsh":
|
||||
return ["--login"];
|
||||
default:
|
||||
|
||||
@@ -8,13 +8,12 @@ import { observer } from "mobx-react";
|
||||
import { Input, InputValidators } from "../input";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import { bundledKubectlPath } from "../../../main/kubectl/kubectl";
|
||||
import { SelectOption, Select } from "../select";
|
||||
import { Switch } from "../switch";
|
||||
import { packageMirrors } from "../../../common/user-store/preferences-helpers";
|
||||
import directoryForBinariesInjectable
|
||||
from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable";
|
||||
import directoryForBinariesInjectable from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { kubectlBinaryPath } from "../../../common/vars";
|
||||
|
||||
interface Dependencies {
|
||||
defaultPathForKubectlBinaries: string;
|
||||
@@ -80,7 +79,7 @@ const NonInjectedKubectlBinaries: React.FC<Dependencies> = observer(({ defaultPa
|
||||
<SubTitle title="Path to kubectl binary" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={bundledKubectlPath()}
|
||||
placeholder={kubectlBinaryPath.get()}
|
||||
value={binariesPath}
|
||||
validators={pathValidator}
|
||||
onChange={setBinariesPath}
|
||||
|
||||
Reference in new issue
Block a user