Fixing Singleton typing to correctly return child class (#1914)

- Add distinction between `getInstance` and `getInstanceOrCreate` since
  it is not always possible to create an instance (since you might not
  know the correct arguments)

- Remove all the `export const *Store = *Store.getInstance<*Store>();`
  calls as it defeats the purpose of `Singleton`. Plus with the typing
  changes the appropriate `*Store.getInstance()` is "short enough".

- Special case the two extension export facades to not need to use
  `getInstanceOrCreate`. Plus since they are just facades it is always
  possible to create them.

- Move some other types to be also `Singleton`'s: ExtensionLoader,
  ExtensionDiscovery, ThemeStore, LocalizationStore, ...

- Fixed dev-run always using the same port with electron inspect

- Update Store documentation with new recommendations about creating
  instances of singletons

- Fix all unit tests to create their dependent singletons

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2021-04-21 09:59:59 -04:00
1 parent b2a570ce28
commit 9563ead2e6
114 files changed
+924 -736

No files matched your search

+132 -106
View File
@@ -1,17 +1,17 @@
import * as tempy from "tempy";
import fs from "fs";
import fse from "fs-extra";
import * as yaml from "js-yaml";
import { promiseExec} from "../promise-exec";
import { helmCli } from "./helm-cli";
import { Cluster } from "../cluster";
import { toCamelCase } from "../../common/utils/camelCase";
export class HelmReleaseManager {
export async function listReleases(pathToKubeconfig: string, namespace?: string) {
const helm = await helmCli.binaryPath();
const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces";
public async listReleases(pathToKubeconfig: string, namespace?: string) {
const helm = await helmCli.binaryPath();
const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces";
const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
try {
const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`);
const output = JSON.parse(stdout);
if (output.length == 0) {
@@ -22,106 +22,132 @@ export class HelmReleaseManager {
});
return output;
}
public async installChart(chart: string, values: any, name: string, namespace: string, version: string, pathToKubeconfig: string){
const helm = await helmCli.binaryPath();
const fileName = tempy.file({name: "values.yaml"});
await fs.promises.writeFile(fileName, yaml.safeDump(values));
try {
let generateName = "";
if (!name) {
generateName = "--generate-name";
name = "";
}
const { stdout } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`).catch((error) => { throw(error.stderr);});
const releaseName = stdout.split("\n")[0].split(" ")[1].trim();
return {
log: stdout,
release: {
name: releaseName,
namespace
}
};
} finally {
await fs.promises.unlink(fileName);
}
}
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 ${proxyKubeconfig}`).catch((error) => { throw(error.stderr);});
return {
log: stdout,
release: this.getRelease(name, namespace, cluster)
};
} finally {
await fs.promises.unlink(fileName);
}
}
public async getRelease(name: string, namespace: string, cluster: Cluster) {
const helm = await helmCli.binaryPath();
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);
return release;
}
public async deleteRelease(name: string, namespace: string, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return stdout;
}
public async getValues(name: string, namespace: string, all: boolean, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath();
const { stdout, } = await promiseExec(`"${helm}" get values ${name} ${all? "--all": ""} --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return stdout;
}
public async getHistory(name: string, namespace: string, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return JSON.parse(stdout);
}
public async rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) {
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`).catch((error) => { throw(error.stderr);});
return stdout;
}
protected async getResources(name: string, namespace: string, cluster: Cluster) {
const helm = await helmCli.binaryPath();
const kubectl = await cluster.kubeCtl.getPath();
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: []})};
});
return stdout;
} catch ({ stderr }) {
throw stderr;
}
}
export const releaseManager = new HelmReleaseManager();
export async function installChart(chart: string, values: any, name: string | undefined, namespace: string, version: string, pathToKubeconfig: string){
const helm = await helmCli.binaryPath();
const fileName = tempy.file({name: "values.yaml"});
await fse.writeFile(fileName, yaml.safeDump(values));
try {
let generateName = "";
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 {
log: stdout,
release: {
name: releaseName,
namespace
}
};
} catch ({ stderr }) {
throw stderr;
} finally {
await fse.unlink(fileName);
}
}
export async function 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"});
await fse.writeFile(fileName, yaml.safeDump(values));
try {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`);
return {
log: stdout,
release: getRelease(name, namespace, cluster)
};
} catch ({ stderr }) {
throw stderr;
} finally {
await fse.unlink(fileName);
}
}
export async function getRelease(name: string, namespace: string, cluster: Cluster) {
try {
const helm = await helmCli.binaryPath();
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`);
const release = JSON.parse(stdout);
release.resources = await getResources(name, namespace, cluster);
return release;
} catch ({ stderr }) {
throw stderr;
}
}
export async function deleteRelease(name: string, namespace: string, pathToKubeconfig: string) {
try {
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
return stdout;
} catch ({ stderr }) {
throw stderr;
}
}
export async function getValues(name: string, namespace: string, all: boolean, pathToKubeconfig: string) {
try {
const helm = await helmCli.binaryPath();
const { stdout, } = await promiseExec(`"${helm}" get values ${name} ${all ? "--all": ""} --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
return stdout;
} catch ({ stderr }) {
throw stderr;
}
}
export async function getHistory(name: string, namespace: string, pathToKubeconfig: string) {
try {
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
return JSON.parse(stdout);
} catch ({ stderr }) {
throw stderr;
}
}
export async function rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) {
try {
const helm = await helmCli.binaryPath();
const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`);
return stdout;
} catch ({ stderr }) {
throw stderr;
}
}
async function getResources(name: string, namespace: string, cluster: Cluster) {
try {
const helm = await helmCli.binaryPath();
const kubectl = await cluster.kubeCtl.getPath();
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`);
return stdout;
} catch {
return { stdout: JSON.stringify({ items: [] }) };
}
}