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

Replace all uses of promiseExec with promiseExecFile (#4514)

This commit is contained in:
Sebastian Malton 2021-12-17 10:29:09 -05:00 committed by GitHub
parent 78678bdf2f
commit e9d99d8485
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 270 additions and 155 deletions

View File

@ -71,7 +71,8 @@ describe("preferences page tests", () => {
} }
}, 10*60*1000); }, 10*60*1000);
utils.itIf(process.platform !== "win32")("ensures helm repos", async () => { // Skipping, but will turn it on again in the follow up PR
it.skip("ensures helm repos", async () => {
await window.click("[data-testid=kubernetes-tab]"); await window.click("[data-testid=kubernetes-tab]");
await window.waitForSelector("[data-testid=repository-name]", { await window.waitForSelector("[data-testid=repository-name]", {
timeout: 140_000, timeout: 140_000,

View File

@ -22,7 +22,7 @@
import { isMac, isWindows } from "./vars"; import { isMac, isWindows } from "./vars";
import wincaAPI from "win-ca/api"; import wincaAPI from "win-ca/api";
import https from "https"; import https from "https";
import { promiseExec } from "./utils/promise-exec"; import { promiseExecFile } from "./utils/promise-exec";
// DST Root CA X3, which was expired on 9.30.2021 // DST Root CA X3, which was expired on 9.30.2021
export const DSTRootCAX3 = "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\nPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\nEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\nrz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\nOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\nxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\naeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\nSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\nikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\nAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\nR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\nJDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\nOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n"; export const DSTRootCAX3 = "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\nPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\nEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\nrz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\nOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\nxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\naeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\nSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\nikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\nAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\nR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\nJDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\nOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n";
@ -33,19 +33,25 @@ export function isCertActive(cert: string) {
return !isExpired; return !isExpired;
} }
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions
const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g;
async function execSecurity(...args: string[]): Promise<string[]> {
const { stdout } = await promiseExecFile("/usr/bin/security", args);
return stdout.split(certSplitPattern);
}
/** /**
* Get root CA certificate from MacOSX system keychain * Get root CA certificate from MacOSX system keychain
* Only return non-expred certificates. * Only return non-expred certificates.
*/ */
export async function getMacRootCA() { export async function getMacRootCA() {
// inspired mac-ca https://github.com/jfromaniello/mac-ca // inspired mac-ca https://github.com/jfromaniello/mac-ca
const args = "find-certificate -a -p"; const [trusted, rootCA] = await Promise.all([
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions execSecurity("find-certificate", "-a", "-p"),
const splitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"),
const systemRootCertsPath = "/System/Library/Keychains/SystemRootCertificates.keychain"; ]);
const bin = "/usr/bin/security";
const trusted = (await promiseExec(`${bin} ${args}`)).stdout.toString().split(splitPattern);
const rootCA = (await promiseExec(`${bin} ${args} ${systemRootCertsPath}`)).stdout.toString().split(splitPattern);
return [...new Set([...trusted, ...rootCA])].filter(isCertActive); return [...new Set([...trusted, ...rootCA])].filter(isCertActive);
} }

View File

@ -20,7 +20,6 @@
*/ */
import * as util from "util"; import * as util from "util";
import { exec, execFile } from "child_process"; import { execFile } from "child_process";
export const promiseExec = util.promisify(exec);
export const promiseExecFile = util.promisify(execFile); export const promiseExecFile = util.promisify(execFile);

View File

@ -22,161 +22,229 @@
import * as tempy from "tempy"; import * as tempy from "tempy";
import fse from "fs-extra"; import fse from "fs-extra";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import { promiseExec } from "../../common/utils/promise-exec"; import { promiseExecFile } from "../../common/utils/promise-exec";
import { helmCli } from "./helm-cli"; import { helmCli } from "./helm-cli";
import type { Cluster } from "../cluster";
import { toCamelCase } from "../../common/utils/camelCase"; import { toCamelCase } from "../../common/utils/camelCase";
import type { BaseEncodingOptions } from "fs";
import { execFile, ExecFileOptions } from "child_process";
export async function listReleases(pathToKubeconfig: string, namespace?: string) { async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
const helm = await helmCli.binaryPath(); const helmCliPath = await helmCli.binaryPath();
const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces";
try { try {
const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`); const { stdout } = await promiseExecFile(helmCliPath, args, options);
const output = JSON.parse(stdout);
if (output.length == 0) { return stdout;
return output;
}
output.forEach((release: any, index: number) => {
output[index] = toCamelCase(release);
});
return output;
} catch (error) { } catch (error) {
throw error?.stderr || error; throw error?.stderr || error;
} }
} }
export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise<Record<string, any>[]> {
const args = [
"ls",
"--output", "json",
];
export async function installChart(chart: string, values: any, name: string | undefined, namespace: string, version: string, pathToKubeconfig: string) { if (namespace) {
const helm = await helmCli.binaryPath(); args.push("-n", namespace);
const fileName = tempy.file({ name: "values.yaml" }); } else {
args.push("--all-namespaces");
}
await fse.writeFile(fileName, yaml.dump(values)); args.push("--kubeconfig", pathToKubeconfig);
const output = JSON.parse(await execHelm(args));
if (!Array.isArray(output) || output.length == 0) {
return [];
}
return output.map(toCamelCase);
}
export async function installChart(chart: string, values: any, name: string | undefined = "", namespace: string, version: string, kubeconfigPath: string) {
const valuesFilePath = tempy.file({ name: "values.yaml" });
await fse.writeFile(valuesFilePath, yaml.dump(values));
const args = ["install"];
if (name) {
args.push(name);
}
args.push(
chart,
"--version", version,
"--values", valuesFilePath,
"--namespace", namespace,
"--kubeconfig", kubeconfigPath,
);
if (!name) {
args.push("--generate-name");
}
try { try {
let generateName = ""; const output = await execHelm(args);
const releaseName = output.split("\n")[0].split(" ")[1].trim();
if (!name) {
generateName = "--generate-name";
name = "";
}
const { stdout } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`);
const releaseName = stdout.split("\n")[0].split(" ")[1].trim();
return { return {
log: stdout, log: output,
release: { release: {
name: releaseName, name: releaseName,
namespace, namespace,
}, },
}; };
} catch (error) {
throw error?.stderr || error;
} finally { } finally {
await fse.unlink(fileName); await fse.unlink(valuesFilePath);
} }
} }
export async function upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, cluster: Cluster) { export async function upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, kubeconfigPath: string, kubectlPath: string) {
const helm = await helmCli.binaryPath(); const valuesFilePath = tempy.file({ name: "values.yaml" });
const fileName = tempy.file({ name: "values.yaml" });
await fse.writeFile(fileName, yaml.dump(values)); await fse.writeFile(valuesFilePath, yaml.dump(values));
const args = [
"upgrade",
name,
chart,
"--version", version,
"--values", valuesFilePath,
"--namespace", namespace,
"--kubeconfig", kubeconfigPath,
];
try { try {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); const output = await execHelm(args);
const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`);
return { return {
log: stdout, log: output,
release: getRelease(name, namespace, cluster), release: getRelease(name, namespace, kubeconfigPath, kubectlPath),
}; };
} catch (error) {
throw error?.stderr || error;
} finally { } finally {
await fse.unlink(fileName); await fse.unlink(valuesFilePath);
} }
} }
export async function getRelease(name: string, namespace: string, cluster: Cluster) { export async function getRelease(name: string, namespace: string, kubeconfigPath: string, kubectlPath: string) {
try { const args = [
const helm = await helmCli.binaryPath(); "status",
const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); name,
"--namespace", namespace,
"--kubeconfig", kubeconfigPath,
"--output", "json",
];
const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`, { const release = JSON.parse(await execHelm(args, {
maxBuffer: 32 * 1024 * 1024 * 1024, // 32 MiB maxBuffer: 32 * 1024 * 1024 * 1024, // 32 MiB
}); }));
const release = JSON.parse(stdout);
release.resources = await getResources(name, namespace, cluster); release.resources = await getResources(name, namespace, kubeconfigPath, kubectlPath);
return release; return release;
} catch (error) {
throw error?.stderr || error;
}
} }
export async function deleteRelease(name: string, namespace: string, pathToKubeconfig: string) { export async function deleteRelease(name: string, namespace: string, kubeconfigPath: string) {
try { return execHelm([
const helm = await helmCli.binaryPath(); "delete",
const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); name,
"--namespace", namespace,
return stdout; "--kubeconfig", kubeconfigPath,
} catch (error) { ]);
throw error?.stderr || error;
}
} }
interface GetValuesOptions { interface GetValuesOptions {
namespace: string; namespace: string;
all?: boolean; all?: boolean;
pathToKubeconfig: string; kubeconfigPath: string;
} }
export async function getValues(name: string, { namespace, all = false, pathToKubeconfig }: GetValuesOptions) { export async function getValues(name: string, { namespace, all = false, kubeconfigPath }: GetValuesOptions) {
try { const args = [
const helm = await helmCli.binaryPath(); "get",
const { stdout } = await promiseExec(`"${helm}" get values ${name} ${all ? "--all" : ""} --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); "values",
name,
];
return stdout; if (all) {
} catch (error) { args.push("--all");
throw error?.stderr || error;
} }
args.push(
"--output", "yaml",
"--namespace", namespace,
"--kubeconfig", kubeconfigPath,
);
return execHelm(args);
} }
export async function getHistory(name: string, namespace: string, pathToKubeconfig: string) { export async function getHistory(name: string, namespace: string, kubeconfigPath: string) {
try { return JSON.parse(await execHelm([
const helm = await helmCli.binaryPath(); "history",
const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); name,
"--output", "json",
return JSON.parse(stdout); "--namespace", namespace,
} catch (error) { "--kubeconfig", kubeconfigPath,
throw error?.stderr || error; ]));
}
} }
export async function rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) { export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string) {
try { return JSON.parse(await execHelm([
const helm = await helmCli.binaryPath(); "rollback",
const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); name,
"--namespace", namespace,
return stdout; "--kubeconfig", kubeconfigPath,
} catch (error) { ]));
throw error?.stderr || error;
}
} }
async function getResources(name: string, namespace: string, cluster: Cluster) { async function getResources(name: string, namespace: string, kubeconfigPath: string, kubectlPath: string) {
try { const helmArgs = [
const helm = await helmCli.binaryPath(); "get",
const kubectl = await cluster.ensureKubectl(); "manifest",
const kubectlPath = await kubectl.getPath(); name,
const pathToKubeconfig = await cluster.getProxyKubeconfigPath(); "--namespace", namespace,
const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectlPath}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`); "--kubeconfig", kubeconfigPath,
];
const kubectlArgs = [
"get",
"--namespace", namespace,
"--kubeconfig", kubeconfigPath,
"-f", "-",
"--output", "json",
];
return JSON.parse(stdout).items; try {
const helmOutput = await execHelm(helmArgs);
return new Promise((resolve, reject) => {
let stdout = "";
let stderr = "";
const kubectl = execFile(kubectlPath, kubectlArgs);
kubectl
.on("exit", (code, signal) => {
if (typeof code === "number") {
if (code === 0) {
resolve(JSON.parse(stdout).items);
} else {
reject(stderr);
}
} else {
reject(new Error(`Kubectl exited with signal ${signal}`));
}
})
.on("error", reject);
kubectl.stderr.on("data", output => stderr += output);
kubectl.stdout.on("data", output => stdout += output);
kubectl.stdin.write(helmOutput);
kubectl.stdin.end();
});
} catch { } catch {
return []; return [];
} }

View File

@ -20,13 +20,14 @@
*/ */
import yaml from "js-yaml"; import yaml from "js-yaml";
import { readFile } from "fs-extra"; import { BaseEncodingOptions, readFile } from "fs-extra";
import { promiseExec } from "../../common/utils/promise-exec"; import { promiseExecFile } from "../../common/utils/promise-exec";
import { helmCli } from "./helm-cli"; import { helmCli } from "./helm-cli";
import { Singleton } from "../../common/utils/singleton"; import { Singleton } from "../../common/utils/singleton";
import { customRequestPromise } from "../../common/request"; import { customRequestPromise } from "../../common/request";
import orderBy from "lodash/orderBy"; import orderBy from "lodash/orderBy";
import logger from "../logger"; import logger from "../logger";
import type { ExecFileOptions } from "child_process";
export type HelmEnv = Record<string, string> & { export type HelmEnv = Record<string, string> & {
HELM_REPOSITORY_CACHE?: string; HELM_REPOSITORY_CACHE?: string;
@ -49,6 +50,18 @@ export interface HelmRepo {
password?: string, 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 { export class HelmRepoManager extends Singleton {
protected repos: HelmRepo[]; protected repos: HelmRepo[];
protected helmEnv: HelmEnv; protected helmEnv: HelmEnv;
@ -77,11 +90,8 @@ export class HelmRepoManager extends Singleton {
} }
protected static async parseHelmEnv() { protected static async parseHelmEnv() {
const helm = await helmCli.binaryPath(); const output = await execHelm(["env"]);
const { stdout } = await promiseExec(`"${helm}" env`).catch((error) => { const lines = output.split(/\r?\n/); // split by new line feed
throw(error.stderr);
});
const lines = stdout.split(/\r?\n/); // split by new line feed
const env: HelmEnv = {}; const env: HelmEnv = {};
lines.forEach((line: string) => { lines.forEach((line: string) => {
@ -135,57 +145,73 @@ export class HelmRepoManager extends Singleton {
cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`, cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`,
})); }));
} catch (error) { } catch (error) {
logger.error(`[HELM]: repositories listing error "${error}"`); logger.error(`[HELM]: repositories listing error`, error);
return []; return [];
} }
} }
public static async update() { public static async update() {
const helm = await helmCli.binaryPath(); return execHelm([
const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => { "repo",
return { stdout: error.stdout }; "update",
}); ]);
return stdout;
} }
public static async addRepo({ name, url }: HelmRepo) { public static async addRepo({ name, url }: HelmRepo) {
logger.info(`[HELM]: adding repo "${name}" from ${url}`); logger.info(`[HELM]: adding repo "${name}" from ${url}`);
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" repo add ${name} ${url}`).catch((error) => {
throw(error.stderr);
});
return stdout; return execHelm([
"repo",
"add",
name,
url,
]);
} }
public static async addCustomRepo(repoAttributes : HelmRepo) { public static async addCustomRepo({ name, url, insecureSkipTlsVerify, username, password, caFile, keyFile, certFile }: HelmRepo) {
logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`); logger.info(`[HELM]: adding repo ${name} from ${url}`);
const helm = await helmCli.binaryPath(); const args = [
"repo",
"add",
name,
url,
];
const insecureSkipTlsVerify = repoAttributes.insecureSkipTlsVerify ? " --insecure-skip-tls-verify" : ""; if (insecureSkipTlsVerify) {
const username = repoAttributes.username ? ` --username "${repoAttributes.username}"` : ""; args.push("--insecure-skip-tls-verify");
const password = repoAttributes.password ? ` --password "${repoAttributes.password}"` : ""; }
const caFile = repoAttributes.caFile ? ` --ca-file "${repoAttributes.caFile}"` : "";
const keyFile = repoAttributes.keyFile ? ` --key-file "${repoAttributes.keyFile}"` : "";
const certFile = repoAttributes.certFile ? ` --cert-file "${repoAttributes.certFile}"` : "";
const addRepoCommand = `"${helm}" repo add ${repoAttributes.name} ${repoAttributes.url}${insecureSkipTlsVerify}${username}${password}${caFile}${keyFile}${certFile}`; if (username) {
const { stdout } = await promiseExec(addRepoCommand).catch((error) => { args.push("--username", username);
throw(error.stderr); }
});
return stdout; if (password) {
args.push("--password", password);
}
if (caFile) {
args.push("--ca-file", caFile);
}
if (keyFile) {
args.push("--key-file", keyFile);
}
if (certFile) {
args.push("--cert-file", certFile);
}
return execHelm(args);
} }
public static async removeRepo({ name, url }: HelmRepo): Promise<string> { public static async removeRepo({ name, url }: HelmRepo): Promise<string> {
logger.info(`[HELM]: removing repo "${name}" from ${url}`); logger.info(`[HELM]: removing repo ${name} (${url})`);
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => {
throw(error.stderr);
});
return stdout; return execHelm([
"repo",
"remove",
name,
]);
} }
} }

View File

@ -65,13 +65,19 @@ class HelmService {
public async listReleases(cluster: Cluster, namespace: string = null) { public async listReleases(cluster: Cluster, namespace: string = null) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("list releases");
return listReleases(proxyKubeconfig, namespace); return listReleases(proxyKubeconfig, namespace);
} }
public async getRelease(cluster: Cluster, releaseName: string, namespace: string) { public async getRelease(cluster: Cluster, releaseName: string, namespace: string) {
const kubeconfigPath = await cluster.getProxyKubeconfigPath();
const kubectl = await cluster.ensureKubectl();
const kubectlPath = await kubectl.getPath();
logger.debug("Fetch release"); logger.debug("Fetch release");
return getRelease(releaseName, namespace, cluster); return getRelease(releaseName, namespace, kubeconfigPath, kubectlPath);
} }
public async getReleaseValues(releaseName: string, { cluster, namespace, all }: GetReleaseValuesArgs) { public async getReleaseValues(releaseName: string, { cluster, namespace, all }: GetReleaseValuesArgs) {
@ -79,7 +85,7 @@ class HelmService {
logger.debug("Fetch release values"); logger.debug("Fetch release values");
return getValues(releaseName, { namespace, all, pathToKubeconfig }); return getValues(releaseName, { namespace, all, kubeconfigPath: pathToKubeconfig });
} }
public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) { public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) {
@ -99,9 +105,13 @@ class HelmService {
} }
public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) { public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
const kubectl = await cluster.ensureKubectl();
const kubectlPath = await kubectl.getPath();
logger.debug("Upgrade release"); logger.debug("Upgrade release");
return upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, cluster); return upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, proxyKubeconfig, kubectlPath);
} }
public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) { public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) {

View File

@ -21,7 +21,7 @@
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
import { promiseExec } from "../common/utils/promise-exec"; import { promiseExecFile } from "../common/utils/promise-exec";
import logger from "./logger"; 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";
@ -199,7 +199,12 @@ export class Kubectl {
if (exists) { if (exists) {
try { try {
const { stdout } = await promiseExec(`"${path}" version --client=true -o json`); const args = [
"version",
"--client", "true",
"--output", "json",
];
const { stdout } = await promiseExecFile(path, args);
const output = JSON.parse(stdout); const output = JSON.parse(stdout);
if (!checkVersion) { if (!checkVersion) {

View File

@ -121,7 +121,7 @@ export class AddHelmRepoDialog extends React.Component<Props> {
<div className="flex gaps align-center"> <div className="flex gaps align-center">
<Input <Input
placeholder={placeholder} placeholder={placeholder}
validators = {isPath} validators={isPath}
className="box grow" className="box grow"
value={this.getFilePath(fileType)} value={this.getFilePath(fileType)}
onChange={v => this.setFilepath(fileType, v)} onChange={v => this.setFilepath(fileType, v)}
@ -172,7 +172,7 @@ export class AddHelmRepoDialog extends React.Component<Props> {
close={this.close} close={this.close}
> >
<Wizard header={header} done={this.close}> <Wizard header={header} done={this.close}>
<WizardStep contentClass="flow column" nextLabel="Add" next={()=>{this.addCustomRepo();}}> <WizardStep contentClass="flow column" nextLabel="Add" next={() => this.addCustomRepo()}>
<div className="flex column gaps"> <div className="flex column gaps">
<Input <Input
autoFocus required autoFocus required