Stop using HelmCli from Renderer (#4861)

* Introduce way for execute file

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make typing of HelmRepo shared

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce way to get Helm environment values

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce function to read YAML file

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce competition for listing active helm repositories in preferences

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make sense in name of injectable

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce helper for opening and selecting values of select

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce competition for activating, deactivating public helm repositories in preferences

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce competition for deactivating helm repository from list of active repositories

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add missing global overrides

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make some tests more deterministic by mocking tooltips

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce competition for activating custom helm repository in preferences

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update snapshots

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove old implementation made redundant with competition for preferences of helm repositories

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add success notification for activating custom helm repository

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce way to get single active helm repository

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract responsibilities from god-class and switch to getting helm repositories using competition instead of another god class

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove dead code

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add TODO

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak position of spinner

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Start handling errors when accessing helm repositories

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Handle error about no helm repositories when updating repositories

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove unwarranted function configuration

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add missing global overrides

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove duplication how to acquire binary path for helm

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove redundant comment

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate naming to match Helm's internal

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Relocate file closer to it's relatives

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne SavolainenandMikko Aspiala authored and GitHub committed 2022-06-13 11:42:53 +03:00
1 parent 5420780ae0
commit 1393cc303d
99 files changed
+21409 -1039

No files matched your search

+43 -15
View File
@@ -5,7 +5,7 @@
import glob from "glob";
import { kebabCase, memoize, noop } from "lodash/fp";
import type { DiContainer } from "@ogre-tools/injectable";
import type { DiContainer, Injectable } 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 appNameInjectable from "./app-paths/app-name/app-name.injectable";
@@ -76,7 +76,7 @@ import quitAndInstallUpdateInjectable from "./electron-app/features/quit-and-ins
import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "./application-update/publish-is-configured.injectable";
import checkForPlatformUpdatesInjectable from "./application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import baseBundeledBinariesDirectoryInjectable from "../common/vars/base-bundled-binaries-dir.injectable";
import baseBundledBinariesDirectoryInjectable from "../common/vars/base-bundled-binaries-dir.injectable";
import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-quit.injectable";
import downloadPlatformUpdateInjectable from "./application-update/download-platform-update/download-platform-update.injectable";
import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.injectable";
@@ -84,6 +84,19 @@ import startKubeConfigSyncInjectable from "./start-main-application/runnables/ku
import appVersionInjectable from "../common/get-configuration-file-model/app-version/app-version.injectable";
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
import periodicalCheckForUpdatesInjectable from "./application-update/periodical-check-for-updates/periodical-check-for-updates.injectable";
import execFileInjectable from "../common/fs/exec-file.injectable";
import normalizedPlatformArchitectureInjectable from "../common/vars/normalized-platform-architecture.injectable";
import getHelmChartInjectable from "./helm/helm-service/get-helm-chart.injectable";
import getHelmChartValuesInjectable from "./helm/helm-service/get-helm-chart-values.injectable";
import listHelmChartsInjectable from "./helm/helm-service/list-helm-charts.injectable";
import deleteHelmReleaseInjectable from "./helm/helm-service/delete-helm-release.injectable";
import getHelmReleaseHistoryInjectable from "./helm/helm-service/get-helm-release-history.injectable";
import getHelmReleaseInjectable from "./helm/helm-service/get-helm-release.injectable";
import getHelmReleaseValuesInjectable from "./helm/helm-service/get-helm-release-values.injectable";
import installHelmChartInjectable from "./helm/helm-service/install-helm-chart.injectable";
import listHelmReleasesInjectable from "./helm/helm-service/list-helm-releases.injectable";
import rollbackHelmReleaseInjectable from "./helm/helm-service/rollback-helm-release.injectable";
import updateHelmReleaseInjectable from "./helm/helm-service/update-helm-release.injectable";
export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) {
const {
@@ -134,6 +147,24 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
di.override(periodicalCheckForUpdatesInjectable, () => ({ start: () => {}, stop: () => {}, started: false }));
overrideFunctionalInjectables(di, [
getHelmChartInjectable,
getHelmChartValuesInjectable,
listHelmChartsInjectable,
deleteHelmReleaseInjectable,
getHelmReleaseHistoryInjectable,
getHelmReleaseInjectable,
getHelmReleaseValuesInjectable,
installHelmChartInjectable,
listHelmReleasesInjectable,
rollbackHelmReleaseInjectable,
updateHelmReleaseInjectable,
writeJsonFileInjectable,
readJsonFileInjectable,
readFileInjectable,
execFileInjectable,
]);
// TODO: Remove usages of globally exported appEventBus to get rid of this
di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>());
@@ -141,7 +172,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
di.override(broadcastMessageInjectable, () => (channel) => {
throw new Error(`Tried to broadcast message to channel "${channel}" over IPC without explicit override.`);
});
di.override(baseBundeledBinariesDirectoryInjectable, () => "some-bin-directory");
di.override(baseBundledBinariesDirectoryInjectable, () => "some-bin-directory");
di.override(spawnInjectable, () => () => {
return {
stderr: { on: jest.fn(), removeAllListeners: jest.fn() },
@@ -150,18 +181,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
} as never;
});
di.override(writeJsonFileInjectable, () => () => {
throw new Error("Tried to write JSON file to file system without specifying explicit override.");
});
di.override(readJsonFileInjectable, () => () => {
throw new Error("Tried to read JSON file from file system without specifying explicit override.");
});
di.override(readFileInjectable, () => () => {
throw new Error("Tried to read file from file system without specifying explicit override.");
});
di.override(loggerInjectable, () => ({
warn: noop,
debug: noop,
@@ -204,6 +223,7 @@ const overrideOperatingSystem = (di: DiContainer) => {
di.override(platformInjectable, () => "darwin");
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
di.override(joinPathsInjectable, () => joinPathsFake);
di.override(normalizedPlatformArchitectureInjectable, () => "arm64");
};
const overrideElectronFeatures = (di: DiContainer) => {
@@ -257,3 +277,11 @@ const overrideElectronFeatures = (di: DiContainer) => {
di.override(publishIsConfiguredInjectable, () => false);
di.override(electronUpdaterIsActiveInjectable, () => false);
};
const overrideFunctionalInjectables = (di: DiContainer, injectables: Injectable<any, any, any>[]) => {
injectables.forEach(injectable => {
di.override(injectable, () => () => {
throw new Error(`Tried to run "${injectable.id}" without explicit override.`);
});
});
};
@@ -4,7 +4,7 @@
*/
import { sortCharts } from "../../../common/utils";
import type { HelmRepo } from "../helm-repo-manager";
import type { HelmRepo } from "../../../common/helm/helm-repo";
const charts = new Map([
["stable", {
+38 -18
View File
@@ -3,27 +3,48 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { helmService } from "../helm-service";
import { HelmRepoManager } from "../helm-repo-manager";
const mockHelmRepoManager = jest.spyOn(HelmRepoManager, "getInstance").mockImplementation();
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import listHelmChartsInjectable from "../helm-service/list-helm-charts.injectable";
import getActiveHelmRepositoriesInjectable from "../repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
import type { AsyncResult } from "../../../common/utils/async-result";
import type { HelmRepo } from "../../../common/helm/helm-repo";
jest.mock("../helm-chart-manager");
describe("Helm Service tests", () => {
let listHelmCharts: () => Promise<any>;
let getActiveHelmRepositoriesMock: jest.Mock<Promise<AsyncResult<HelmRepo[]>>>;
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
getActiveHelmRepositoriesMock = jest.fn();
di.override(getActiveHelmRepositoriesInjectable, () => getActiveHelmRepositoriesMock);
di.unoverride(listHelmChartsInjectable);
di.permitSideEffects(listHelmChartsInjectable);
listHelmCharts = di.inject(listHelmChartsInjectable);
});
afterEach(() => {
jest.resetAllMocks();
});
it("list charts with deprecated entries", async () => {
mockHelmRepoManager.mockReturnValue({
repositories: jest.fn().mockImplementation(async () => [
{ name: "stable", url: "stableurl" },
{ name: "experiment", url: "experimenturl" },
]),
} as any);
getActiveHelmRepositoriesMock.mockReturnValue(
Promise.resolve({
callWasSuccessful: true,
const charts = await helmService.listCharts();
response: [
{ name: "stable", url: "stableurl" },
{ name: "experiment", url: "experimenturl" },
],
}),
);
const charts = await listHelmCharts();
expect(charts).toEqual({
stable: {
@@ -123,15 +144,14 @@ describe("Helm Service tests", () => {
});
it("list charts sorted by version in descending order", async () => {
mockHelmRepoManager.mockReturnValue({
repositories: jest.fn().mockImplementation(async () => {
return [
{ name: "bitnami", url: "bitnamiurl" },
];
getActiveHelmRepositoriesMock.mockReturnValue(
Promise.resolve({
callWasSuccessful: true,
response: [{ name: "bitnami", url: "bitnamiurl" }],
}),
} as any);
);
const charts = await helmService.listCharts();
const charts = await listHelmCharts();
expect(charts).toEqual({
bitnami: {
@@ -0,0 +1,31 @@
/**
* 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 execFileInjectable from "../../../common/fs/exec-file.injectable";
import helmBinaryPathInjectable from "../helm-binary-path.injectable";
import type { AsyncResult } from "../../../common/utils/async-result";
import { getErrorMessage } from "../../../common/utils/get-error-message";
const execHelmInjectable = getInjectable({
id: "exec-helm",
instantiate: (di) => {
const execFile = di.inject(execFileInjectable);
const helmBinaryPath = di.inject(helmBinaryPathInjectable);
return async (...args: string[]): Promise<AsyncResult<string>> => {
try {
const response = await execFile(helmBinaryPath, args);
return { callWasSuccessful: true, response };
} catch (error) {
return { callWasSuccessful: false, error: getErrorMessage(error) };
}
};
},
});
export default execHelmInjectable;
@@ -0,0 +1,43 @@
/**
* 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 execHelmInjectable from "../exec-helm/exec-helm.injectable";
import type { AsyncResult } from "../../../common/utils/async-result";
export type HelmEnv = Record<string, string> & {
HELM_REPOSITORY_CACHE?: string;
HELM_REPOSITORY_CONFIG?: string;
};
const getHelmEnvInjectable = getInjectable({
id: "get-helm-env",
instantiate: (di) => {
const execHelm = di.inject(execHelmInjectable);
return async (): Promise<AsyncResult<HelmEnv>> => {
const result = await execHelm("env");
if (!result.callWasSuccessful) {
return { callWasSuccessful: false, error: result.error };
}
const lines = result.response.split(/\r?\n/); // split by new line feed
const env: HelmEnv = {};
lines.forEach((line: string) => {
const [key, value] = line.split("=");
if (key && value) {
env[key] = value.replace(/"/g, ""); // strip quotas
}
});
return { callWasSuccessful: true, response: env };
};
},
});
export default getHelmEnvInjectable;
@@ -0,0 +1,25 @@
/**
* 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 { getBinaryName } from "../../common/vars";
import getAbsolutePathInjectable from "../../common/path/get-absolute-path.injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import baseBundledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
const helmBinaryPathInjectable = getInjectable({
id: "helm-binary-path",
instantiate: (di) => {
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
const normalizedPlatform = di.inject(normalizedPlatformInjectable);
const baseBundledBinariesDirectory = di.inject(baseBundledBinariesDirectoryInjectable);
const helmBinaryName = getBinaryName("helm", { forPlatform: normalizedPlatform });
return getAbsolutePath(baseBundledBinariesDirectory, helmBinaryName);
},
});
export default helmBinaryPathInjectable;
+1 -1
View File
@@ -5,13 +5,13 @@
import fs from "fs";
import * as yaml from "js-yaml";
import type { HelmRepo } from "./helm-repo-manager";
import logger from "../logger";
import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api";
import { iter, put, sortCharts } from "../../common/utils";
import { execHelm } from "./exec";
import type { SetRequired } from "type-fest";
import { assert } from "console";
import type { HelmRepo } from "../../common/helm/helm-repo";
interface ChartCacheEntry {
data: string; // serialized JSON
@@ -1,172 +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 yaml from "js-yaml";
import { readFile } from "fs-extra";
import { customRequestPromise } from "../../common/request";
import orderBy from "lodash/orderBy";
import logger from "../logger";
import { execHelm } from "./exec";
import type { HelmEnv, HelmRepo, HelmRepoConfig } from "./helm-repo-manager";
interface EnsuredHelmRepoManagerData {
helmEnv: HelmEnv;
didUpdateOnce: boolean;
}
export class HelmRepoManager {
protected helmEnv?: HelmEnv;
protected didUpdateOnce?: boolean;
public async loadAvailableRepos(): Promise<HelmRepo[]> {
const res = await customRequestPromise({
uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json",
json: true,
resolveWithFullResponse: true,
timeout: 10000,
});
return orderBy(res.body as HelmRepo[], repo => repo.name);
}
private async ensureInitialized(): Promise<EnsuredHelmRepoManagerData> {
this.helmEnv ??= await this.parseHelmEnv();
const repos = await this.list(this.helmEnv);
if (repos.length === 0) {
await this.addRepo({
name: "bitnami",
url: "https://charts.bitnami.com/bitnami",
});
}
if (!this.didUpdateOnce) {
await this.update();
this.didUpdateOnce = true;
}
return {
didUpdateOnce: this.didUpdateOnce,
helmEnv: this.helmEnv,
};
}
protected async parseHelmEnv() {
const output = await execHelm(["env"]);
const lines = output.split(/\r?\n/); // split by new line feed
const env: Partial<Record<string, string>> = {};
lines.forEach((line: string) => {
const [key, value] = line.split("=");
if (key && value) {
env[key] = value.replace(/"/g, ""); // strip quotas
}
});
return env as HelmEnv;
}
public async repo(name: string): Promise<HelmRepo | undefined> {
const repos = await this.repositories();
return repos.find(repo => repo.name === name);
}
private async list(helmEnv: HelmEnv): Promise<HelmRepo[]> {
try {
const rawConfig = await readFile(helmEnv.HELM_REPOSITORY_CONFIG, "utf8");
const parsedConfig = yaml.load(rawConfig) as HelmRepoConfig;
if (typeof parsedConfig === "object" && parsedConfig) {
return parsedConfig.repositories;
}
} catch {
// ignore error
}
return [];
}
public async repositories(): Promise<HelmRepo[]> {
try {
const { helmEnv } = await this.ensureInitialized();
const repos = await this.list(helmEnv);
return repos.map(repo => ({
...repo,
cacheFilePath: `${helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`,
}));
} catch (error) {
logger.error(`[HELM]: repositories listing error`, error);
return [];
}
}
public async update() {
return execHelm([
"repo",
"update",
]);
}
public async addRepo({ name, url, insecureSkipTlsVerify, username, password, caFile, keyFile, certFile }: HelmRepo) {
logger.info(`[HELM]: adding repo ${name} from ${url}`);
const args = [
"repo",
"add",
name,
url,
];
if (insecureSkipTlsVerify) {
args.push("--insecure-skip-tls-verify");
}
if (username) {
args.push("--username", username);
}
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 async removeRepo({ name, url }: HelmRepo): Promise<string> {
logger.info(`[HELM]: removing repo ${name} (${url})`);
return execHelm([
"repo",
"remove",
name,
]);
}
}
const helmRepoManagerInjectable = getInjectable({
id: "helm-repo-manager",
instantiate: () => new HelmRepoManager(),
});
export default helmRepoManagerInjectable;
-33
View File
@@ -1,33 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import {
asLegacyGlobalSingletonForExtensionApi,
} from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api";
import helmRepoManagerInjectable from "./helm-repo-manager.injectable";
export type HelmEnv = Partial<Record<string, string>> & {
HELM_REPOSITORY_CACHE: string;
HELM_REPOSITORY_CONFIG: string;
};
export interface HelmRepoConfig {
repositories: HelmRepo[];
}
export interface HelmRepo {
name: string;
url: string;
cacheFilePath?: string;
caFile?: string;
certFile?: string;
insecureSkipTlsVerify?: boolean;
keyFile?: string;
username?: string;
password?: string;
}
export const HelmRepoManager = asLegacyGlobalSingletonForExtensionApi(helmRepoManagerInjectable);
-134
View File
@@ -1,134 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { Cluster } from "../../common/cluster/cluster";
import logger from "../logger";
import { HelmRepoManager } from "./helm-repo-manager";
import { HelmChartManager } from "./helm-chart-manager";
import { deleteRelease, getHistory, getRelease, getValues, installChart, listReleases, rollback, upgradeRelease } from "./helm-release-manager";
import type { JsonObject } from "type-fest";
import { object } from "../../common/utils";
interface GetReleaseValuesArgs {
cluster: Cluster;
namespace: string;
all: boolean;
}
export interface InstallChartArgs {
chart: string;
values: JsonObject;
name: string;
namespace: string;
version: string;
}
export interface UpdateChartArgs {
chart: string;
values: JsonObject;
version: string;
}
class HelmService {
public async installChart(cluster: Cluster, data: InstallChartArgs) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
return installChart(data.chart, data.values, data.name, data.namespace, data.version, proxyKubeconfig);
}
public async listCharts() {
const repositories = await HelmRepoManager.getInstance().repositories();
return object.fromEntries(
await Promise.all(repositories.map(async repo => [repo.name, await HelmChartManager.forRepo(repo).charts()] as const)),
);
}
public async getChart(repoName: string, chartName: string, version = "") {
const repo = await HelmRepoManager.getInstance().repo(repoName);
if (!repo) {
return undefined;
}
const chartManager = HelmChartManager.forRepo(repo);
return {
readme: await chartManager.getReadme(chartName, version),
versions: await chartManager.chartVersions(chartName),
};
}
public async getChartValues(repoName: string, chartName: string, version = "") {
const repo = await HelmRepoManager.getInstance().repo(repoName);
if (!repo) {
return undefined;
}
return HelmChartManager.forRepo(repo).getValues(chartName, version);
}
public async listReleases(cluster: Cluster, namespace?: string) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("list releases");
return listReleases(proxyKubeconfig, namespace);
}
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");
return getRelease(releaseName, namespace, kubeconfigPath, kubectlPath);
}
public async getReleaseValues(releaseName: string, { cluster, namespace, all }: GetReleaseValuesArgs) {
const pathToKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Fetch release values");
return getValues(releaseName, { namespace, all, kubeconfigPath: pathToKubeconfig });
}
public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Fetch release history");
return getHistory(releaseName, namespace, proxyKubeconfig);
}
public async deleteRelease(cluster: Cluster, releaseName: string, namespace: string) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Delete release");
return deleteRelease(releaseName, namespace, proxyKubeconfig);
}
public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: UpdateChartArgs) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
const kubectl = await cluster.ensureKubectl();
const kubectlPath = await kubectl.getPath();
logger.debug("Upgrade release");
return upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, proxyKubeconfig, kubectlPath);
}
public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Rollback release");
await rollback(releaseName, namespace, revision, proxyKubeconfig);
}
}
export const helmService = new HelmService();
@@ -0,0 +1,28 @@
/**
* 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 type { Cluster } from "../../../common/cluster/cluster";
import { deleteRelease } from "../helm-release-manager";
import loggerInjectable from "../../../common/logger.injectable";
const deleteHelmReleaseInjectable = getInjectable({
id: "delete-helm-release",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (cluster: Cluster, releaseName: string, namespace: string) => {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Delete release");
return deleteRelease(releaseName, namespace, proxyKubeconfig);
};
},
causesSideEffects: true,
});
export default deleteHelmReleaseInjectable;
@@ -0,0 +1,29 @@
/**
* 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 { HelmChartManager } from "../helm-chart-manager";
import getActiveHelmRepositoryInjectable from "../repositories/get-active-helm-repository.injectable";
const getHelmChartValuesInjectable = getInjectable({
id: "get-helm-chart-values",
instantiate: (di) => {
const getActiveHelmRepository = di.inject(getActiveHelmRepositoryInjectable);
return async (repoName: string, chartName: string, version = "") => {
const repo = await getActiveHelmRepository(repoName);
if (!repo) {
return undefined;
}
return HelmChartManager.forRepo(repo).getValues(chartName, version);
};
},
causesSideEffects: true,
});
export default getHelmChartValuesInjectable;
@@ -0,0 +1,34 @@
/**
* 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 { HelmChartManager } from "../helm-chart-manager";
import getActiveHelmRepositoryInjectable from "../repositories/get-active-helm-repository.injectable";
const getHelmChartInjectable = getInjectable({
id: "get-helm-chart",
instantiate: (di) => {
const getActiveHelmRepository = di.inject(getActiveHelmRepositoryInjectable);
return async (repoName: string, chartName: string, version = "") => {
const repo = await getActiveHelmRepository(repoName);
if (!repo) {
return undefined;
}
const chartManager = HelmChartManager.forRepo(repo);
return {
readme: await chartManager.getReadme(chartName, version),
versions: await chartManager.chartVersions(chartName),
};
};
},
causesSideEffects: true,
});
export default getHelmChartInjectable;
@@ -0,0 +1,28 @@
/**
* 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 type { Cluster } from "../../../common/cluster/cluster";
import { getHistory } from "../helm-release-manager";
import loggerInjectable from "../../../common/logger.injectable";
const getHelmReleaseHistoryInjectable = getInjectable({
id: "get-helm-release-history",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (cluster: Cluster, releaseName: string, namespace: string) => {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Fetch release history");
return getHistory(releaseName, namespace, proxyKubeconfig);
};
},
causesSideEffects: true,
});
export default getHelmReleaseHistoryInjectable;
@@ -0,0 +1,41 @@
/**
* 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 { getValues } from "../helm-release-manager";
import loggerInjectable from "../../../common/logger.injectable";
import type { Cluster } from "../../../common/cluster/cluster";
interface GetReleaseValuesArgs {
cluster: Cluster;
namespace: string;
all: boolean;
}
const getHelmReleaseValuesInjectable = getInjectable({
id: "get-helm-release-values",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (
releaseName: string,
{ cluster, namespace, all }: GetReleaseValuesArgs,
) => {
const pathToKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Fetch release values");
return getValues(releaseName, {
namespace,
all,
kubeconfigPath: pathToKubeconfig,
});
};
},
causesSideEffects: true,
});
export default getHelmReleaseValuesInjectable;
@@ -0,0 +1,30 @@
/**
* 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 type { Cluster } from "../../../common/cluster/cluster";
import { getRelease } from "../helm-release-manager";
import loggerInjectable from "../../../common/logger.injectable";
const getHelmReleaseInjectable = getInjectable({
id: "get-helm-release",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (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");
return getRelease(releaseName, namespace, kubeconfigPath, kubectlPath);
};
},
causesSideEffects: true,
});
export default getHelmReleaseInjectable;
@@ -0,0 +1,30 @@
/**
* 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 type { JsonObject } from "type-fest";
import type { Cluster } from "../../../common/cluster/cluster";
import { installChart } from "../helm-release-manager";
export interface InstallChartArgs {
chart: string;
values: JsonObject;
name: string;
namespace: string;
version: string;
}
const installHelmChartInjectable = getInjectable({
id: "install-helm-chart",
instantiate: () => async (cluster: Cluster, data: InstallChartArgs) => {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
return installChart(data.chart, data.values, data.name, data.namespace, data.version, proxyKubeconfig);
},
causesSideEffects: true,
});
export default installHelmChartInjectable;
@@ -0,0 +1,41 @@
/**
* 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 assert from "assert";
import { object } from "../../../common/utils";
import { HelmChartManager } from "../helm-chart-manager";
import getActiveHelmRepositoriesInjectable from "../repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
const listHelmChartsInjectable = getInjectable({
id: "list-helm-charts",
instantiate: (di) => {
const getActiveHelmRepositories = di.inject(getActiveHelmRepositoriesInjectable);
return async () => {
const result = await getActiveHelmRepositories();
assert(result.callWasSuccessful);
const repositories = result.response;
return object.fromEntries(
await Promise.all(
repositories.map(
async (repo) =>
[
repo.name,
await HelmChartManager.forRepo(repo).charts(),
] as const,
),
),
);
};
},
causesSideEffects: true,
});
export default listHelmChartsInjectable;
@@ -0,0 +1,28 @@
/**
* 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 type { Cluster } from "../../../common/cluster/cluster";
import loggerInjectable from "../../../common/logger.injectable";
import { listReleases } from "../helm-release-manager";
const listHelmReleasesInjectable = getInjectable({
id: "list-helm-releases",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (cluster: Cluster, namespace?: string) => {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("list releases");
return listReleases(proxyKubeconfig, namespace);
};
},
causesSideEffects: true,
});
export default listHelmReleasesInjectable;
@@ -0,0 +1,32 @@
/**
* 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 type { Cluster } from "../../../common/cluster/cluster";
import loggerInjectable from "../../../common/logger.injectable";
import { rollback } from "../helm-release-manager";
const rollbackHelmReleaseInjectable = getInjectable({
id: "rollback-helm-release",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (
cluster: Cluster,
releaseName: string,
namespace: string,
revision: number,
) => {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
logger.debug("Rollback release");
await rollback(releaseName, namespace, revision, proxyKubeconfig);
};
},
causesSideEffects: true,
});
export default rollbackHelmReleaseInjectable;
@@ -0,0 +1,45 @@
/**
* 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 type { Cluster } from "../../../common/cluster/cluster";
import { upgradeRelease } from "../helm-release-manager";
import loggerInjectable from "../../../common/logger.injectable";
import type { JsonObject } from "type-fest";
export interface UpdateChartArgs {
chart: string;
values: JsonObject;
version: string;
}
const updateHelmReleaseInjectable = getInjectable({
id: "update-helm-release",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
return async (cluster: Cluster, releaseName: string, namespace: string, data: UpdateChartArgs) => {
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
const kubectl = await cluster.ensureKubectl();
const kubectlPath = await kubectl.getPath();
logger.debug("Upgrade release");
return upgradeRelease(
releaseName,
data.chart,
data.values,
namespace,
data.version,
proxyKubeconfig,
kubectlPath,
);
};
},
causesSideEffects: true,
});
export default updateHelmReleaseInjectable;
@@ -0,0 +1,26 @@
/**
* 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 addHelmRepositoryChannelInjectable from "../../../../common/helm/add-helm-repository-channel.injectable";
import addHelmRepositoryInjectable from "./add-helm-repository.injectable";
import { requestChannelListenerInjectionToken } from "../../../../common/utils/channel/request-channel-listener-injection-token";
const addHelmRepositoryChannelListenerInjectable = getInjectable({
id: "add-helm-repository-channel-listener",
instantiate: (di) => {
const addHelmRepository = di.inject(addHelmRepositoryInjectable);
const channel = di.inject(addHelmRepositoryChannelInjectable);
return {
channel,
handler: addHelmRepository,
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default addHelmRepositoryChannelListenerInjectable;
@@ -0,0 +1,62 @@
/**
* 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 execHelmInjectable from "../../exec-helm/exec-helm.injectable";
import type { HelmRepo } from "../../../../common/helm/helm-repo";
import loggerInjectable from "../../../../common/logger.injectable";
const addHelmRepositoryInjectable = getInjectable({
id: "add-helm-repository",
instantiate: (di) => {
const execHelm = di.inject(execHelmInjectable);
const logger = di.inject(loggerInjectable);
return async (repo: HelmRepo) => {
const {
name,
url,
insecureSkipTlsVerify,
username,
password,
caFile,
keyFile,
certFile,
} = repo;
logger.info(`[HELM]: adding repo ${name} from ${url}`);
const args = ["repo", "add", name, url];
if (insecureSkipTlsVerify) {
args.push("--insecure-skip-tls-verify");
}
if (username) {
args.push("--username", username);
}
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 await execHelm(...args);
};
},
});
export default addHelmRepositoryInjectable;
@@ -0,0 +1,26 @@
/**
* 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 { requestChannelListenerInjectionToken } from "../../../../common/utils/channel/request-channel-listener-injection-token";
import getActiveHelmRepositoriesChannelInjectable from "../../../../common/helm/get-active-helm-repositories-channel.injectable";
import getActiveHelmRepositoriesInjectable from "./get-active-helm-repositories.injectable";
const getActiveHelmRepositoriesChannelListenerInjectable = getInjectable({
id: "get-active-helm-repositories-channel-listener",
instantiate: (di) => {
const getActiveHelmRepositories = di.inject(getActiveHelmRepositoriesInjectable);
return {
channel: di.inject(getActiveHelmRepositoriesChannelInjectable),
handler: getActiveHelmRepositories,
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default getActiveHelmRepositoriesChannelListenerInjectable;
@@ -0,0 +1,131 @@
/**
* 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 type { HelmRepo } from "../../../../common/helm/helm-repo";
import type { ReadYamlFile } from "../../../../common/fs/read-yaml-file.injectable";
import readYamlFileInjectable from "../../../../common/fs/read-yaml-file.injectable";
import getHelmEnvInjectable from "../../get-helm-env/get-helm-env.injectable";
import execHelmInjectable from "../../exec-helm/exec-helm.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
import type { AsyncResult } from "../../../../common/utils/async-result";
interface HelmRepositoryFromYaml {
name: string;
url: string;
caFile: string;
certFile: string;
insecure_skip_tls_verify: boolean;
keyFile: string;
pass_credentials_all: boolean;
password: string;
username: string;
}
export interface HelmRepositoriesFromYaml {
repositories: HelmRepositoryFromYaml[];
}
const getActiveHelmRepositoriesInjectable = getInjectable({
id: "get-helm-repositories",
instantiate: (di) => {
const readYamlFile = di.inject(readYamlFileInjectable);
const execHelm = di.inject(execHelmInjectable);
const getHelmEnv = di.inject(getHelmEnvInjectable);
const logger = di.inject(loggerInjectable);
const getRepositories = getRepositoriesFor(readYamlFile);
return async (): Promise<AsyncResult<HelmRepo[]>> => {
const envResult = await getHelmEnv();
if (!envResult.callWasSuccessful) {
return {
callWasSuccessful: false,
error: `Error getting Helm configuration: ${envResult.error}`,
};
}
const {
HELM_REPOSITORY_CONFIG: repositoryConfigFilePath,
HELM_REPOSITORY_CACHE: helmRepositoryCacheDirPath,
} = envResult.response;
if (!repositoryConfigFilePath) {
const errorMessage = "Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`.";
logger.error(errorMessage);
return {
callWasSuccessful: false,
error: `Error getting Helm configuration: ${errorMessage}`,
};
}
if (!helmRepositoryCacheDirPath) {
const errorMessage = "Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`.";
logger.error(errorMessage);
return {
callWasSuccessful: false,
error: `Error getting Helm configuration: ${errorMessage}`,
};
}
const updateResult = await execHelm("repo", "update");
if (!updateResult.callWasSuccessful) {
if (!updateResult.error.includes(internalHelmErrorForNoRepositoriesFound)) {
return {
callWasSuccessful: false,
error: `Error updating Helm repositories: ${updateResult.error}`,
};
}
const resultOfAddingDefaultRepository = await execHelm("repo", "add", "bitnami", "https://charts.bitnami.com/bitnami");
if (!resultOfAddingDefaultRepository.callWasSuccessful) {
return {
callWasSuccessful: false,
error: `Error when adding default Helm repository: ${resultOfAddingDefaultRepository.error}`,
};
}
}
return {
callWasSuccessful: true,
response: await getRepositories(
repositoryConfigFilePath,
helmRepositoryCacheDirPath,
),
};
};
},
});
export default getActiveHelmRepositoriesInjectable;
const getRepositoriesFor =
(readYamlFile: ReadYamlFile) =>
async (repositoryConfigFilePath: string, helmRepositoryCacheDirPath: string): Promise<HelmRepo[]> => {
const { repositories } = (await readYamlFile(
repositoryConfigFilePath,
)) as HelmRepositoriesFromYaml;
return repositories.map((repository) => ({
name: repository.name,
url: repository.url,
caFile: repository.caFile,
certFile: repository.certFile,
insecureSkipTlsVerify: repository.insecure_skip_tls_verify,
keyFile: repository.keyFile,
username: repository.username,
password: repository.password,
cacheFilePath: `${helmRepositoryCacheDirPath}/${repository.name}-index.yaml`,
}));
};
const internalHelmErrorForNoRepositoriesFound = "no repositories found. You must add one before updating";
@@ -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";
import assert from "assert";
import getActiveHelmRepositoriesInjectable from "./get-active-helm-repositories/get-active-helm-repositories.injectable";
const getActiveHelmRepositoryInjectable = getInjectable({
id: "get-active-helm-repository",
instantiate: (di) => {
const getActiveHelmRepositories = di.inject(getActiveHelmRepositoriesInjectable);
return async (name: string) => {
const activeHelmRepositories = await getActiveHelmRepositories();
assert(activeHelmRepositories.callWasSuccessful);
return activeHelmRepositories.response.find(
(repository) => repository.name === name,
);
};
},
});
export default getActiveHelmRepositoryInjectable;
@@ -0,0 +1,26 @@
/**
* 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 { requestChannelListenerInjectionToken } from "../../../../common/utils/channel/request-channel-listener-injection-token";
import removeHelmRepositoryInjectable from "./remove-helm-repository.injectable";
import removeHelmRepositoryChannelInjectable from "../../../../common/helm/remove-helm-repository-channel.injectable";
const removeHelmRepositoryChannelListenerInjectable = getInjectable({
id: "remove-helm-repository-channel-listener",
instantiate: (di) => {
const removeHelmRepository = di.inject(removeHelmRepositoryInjectable);
const channel = di.inject(removeHelmRepositoryChannelInjectable);
return {
channel,
handler: removeHelmRepository,
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default removeHelmRepositoryChannelListenerInjectable;
@@ -0,0 +1,29 @@
/**
* 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 execHelmInjectable from "../../exec-helm/exec-helm.injectable";
import type { HelmRepo } from "../../../../common/helm/helm-repo";
import loggerInjectable from "../../../../common/logger.injectable";
const removeHelmRepositoryInjectable = getInjectable({
id: "remove-helm-repository",
instantiate: (di) => {
const execHelm = di.inject(execHelmInjectable);
const logger = di.inject(loggerInjectable);
return async (repo: HelmRepo) => {
logger.info(`[HELM]: removing repo ${repo.name} (${repo.url})`);
return execHelm(
"repo",
"remove",
repo.name,
);
};
},
});
export default removeHelmRepositoryInjectable;
@@ -12,7 +12,7 @@ import { getBinaryName } from "../../common/vars";
import spawnInjectable from "../child-process/spawn.injectable";
import { getKubeAuthProxyCertificate } from "./get-kube-auth-proxy-certificate";
import loggerInjectable from "../../common/logger.injectable";
import baseBundeledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
import baseBundledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy;
@@ -25,7 +25,7 @@ const createKubeAuthProxyInjectable = getInjectable({
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => {
const clusterUrl = new URL(cluster.apiUrl);
const dependencies: KubeAuthProxyDependencies = {
proxyBinPath: path.join(di.inject(baseBundeledBinariesDirectoryInjectable), binaryName),
proxyBinPath: path.join(di.inject(baseBundledBinariesDirectoryInjectable), binaryName),
proxyCert: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate),
spawn: di.inject(spawnInjectable),
logger: di.inject(loggerInjectable),
@@ -4,13 +4,13 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import path from "path";
import baseBundeledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
import baseBundledBinariesDirectoryInjectable 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(baseBundledBinariesDirectoryInjectable),
di.inject(kubectlBinaryNameInjectable),
),
});
@@ -11,7 +11,7 @@ import kubectlDownloadingNormalizedArchInjectable from "./normalized-arch.inject
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";
import baseBundledBinariesDirectoryInjectable from "../../common/vars/base-bundled-binaries-dir.injectable";
const createKubectlInjectable = getInjectable({
id: "create-kubectl",
@@ -24,7 +24,7 @@ const createKubectlInjectable = getInjectable({
normalizedDownloadPlatform: di.inject(normalizedPlatformInjectable),
kubectlBinaryName: di.inject(kubectlBinaryNameInjectable),
bundledKubectlBinaryPath: di.inject(bundledKubectlBinaryPathInjectable),
baseBundeledBinariesDirectory: di.inject(baseBundeledBinariesDirectoryInjectable),
baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable),
};
return (clusterVersion: string) => new Kubectl(dependencies, clusterVersion);
@@ -5,25 +5,29 @@
import { getRouteInjectable } from "../../../router/router.injectable";
import { apiPrefix } from "../../../../common/vars";
import { route } from "../../../router/route";
import { helmService } from "../../../helm/helm-service";
import getHelmChartInjectable from "../../../helm/helm-service/get-helm-chart.injectable";
const getChartRouteInjectable = getRouteInjectable({
id: "get-chart-route",
instantiate: () => route({
method: "get",
path: `${apiPrefix}/v2/charts/{repo}/{chart}`,
})(async ({ params, query }) => {
const { repo, chart } = params;
instantiate: (di) => {
const getHelmChart = di.inject(getHelmChartInjectable);
return {
response: await helmService.getChart(
repo,
chart,
query.get("version") ?? undefined,
),
};
}),
return route({
method: "get",
path: `${apiPrefix}/v2/charts/{repo}/{chart}`,
})(async ({ params, query }) => {
const { repo, chart } = params;
return {
response: await getHelmChart(
repo,
chart,
query.get("version") ?? undefined,
),
};
});
},
});
export default getChartRouteInjectable;
@@ -3,23 +3,27 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getRouteInjectable } from "../../../router/router.injectable";
import { helmService } from "../../../helm/helm-service";
import { apiPrefix } from "../../../../common/vars";
import { route } from "../../../router/route";
import getHelmChartValuesInjectable from "../../../helm/helm-service/get-helm-chart-values.injectable";
const getChartRouteValuesInjectable = getRouteInjectable({
id: "get-chart-route-values",
instantiate: () => route({
method: "get",
path: `${apiPrefix}/v2/charts/{repo}/{chart}/values`,
})(async ({ params, query }) => ({
response: await helmService.getChartValues(
params.repo,
params.chart,
query.get("version") ?? undefined,
),
})),
instantiate: (di) => {
const getHelmChartValues = di.inject(getHelmChartValuesInjectable);
return route({
method: "get",
path: `${apiPrefix}/v2/charts/{repo}/{chart}/values`,
})(async ({ params, query }) => ({
response: await getHelmChartValues(
params.repo,
params.chart,
query.get("version") ?? undefined,
),
}));
},
});
export default getChartRouteValuesInjectable;
@@ -3,19 +3,23 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getRouteInjectable } from "../../../router/router.injectable";
import { helmService } from "../../../helm/helm-service";
import { apiPrefix } from "../../../../common/vars";
import { route } from "../../../router/route";
import listHelmChartsInjectable from "../../../helm/helm-service/list-helm-charts.injectable";
const listChartsRouteInjectable = getRouteInjectable({
id: "list-charts-route",
instantiate: () => route({
method: "get",
path: `${apiPrefix}/v2/charts`,
})(async () => ({
response: await helmService.listCharts(),
})),
instantiate: (di) => {
const listHelmCharts = di.inject(listHelmChartsInjectable);
return route({
method: "get",
path: `${apiPrefix}/v2/charts`,
})(async () => ({
response: await listHelmCharts(),
}));
},
});
export default listChartsRouteInjectable;
@@ -3,23 +3,23 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import { clusterRoute } from "../../../router/route";
import deleteHelmReleaseInjectable from "../../../helm/helm-service/delete-helm-release.injectable";
const deleteReleaseRouteInjectable = getRouteInjectable({
id: "delete-release-route",
instantiate: () => clusterRoute({
method: "delete",
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
})(async ({ cluster, params: { release, namespace }}) => ({
response: await helmService.deleteRelease(
cluster,
release,
namespace,
),
})),
instantiate: (di) => {
const deleteHelmRelease = di.inject(deleteHelmReleaseInjectable);
return clusterRoute({
method: "delete",
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
})(async ({ cluster, params: { release, namespace }}) => ({
response: await deleteHelmRelease(cluster, release, namespace),
}));
},
});
export default deleteReleaseRouteInjectable;
@@ -3,23 +3,27 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import { clusterRoute } from "../../../router/route";
import getHelmReleaseHistoryInjectable from "../../../helm/helm-service/get-helm-release-history.injectable";
const getReleaseRouteHistoryInjectable = getRouteInjectable({
id: "get-release-history-route",
instantiate: () => clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace}/{release}/history`,
})(async ({ cluster, params }) => ({
response: await helmService.getReleaseHistory(
cluster,
params.release,
params.namespace,
),
})),
instantiate: (di) => {
const getHelmReleaseHistory = di.inject(getHelmReleaseHistoryInjectable);
return clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace}/{release}/history`,
})(async ({ cluster, params }) => ({
response: await getHelmReleaseHistory(
cluster,
params.release,
params.namespace,
),
}));
},
});
export default getReleaseRouteHistoryInjectable;
@@ -3,23 +3,27 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import { clusterRoute } from "../../../router/route";
import getHelmReleaseInjectable from "../../../helm/helm-service/get-helm-release.injectable";
const getReleaseRouteInjectable = getRouteInjectable({
id: "get-release-route",
instantiate: () => clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
})(async ({ cluster, params }) => ({
response: await helmService.getRelease(
cluster,
params.release,
params.namespace,
),
})),
instantiate: (di) => {
const getHelmRelease = di.inject(getHelmReleaseInjectable);
return clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
})(async ({ cluster, params }) => ({
response: await getHelmRelease(
cluster,
params.release,
params.namespace,
),
}));
},
});
export default getReleaseRouteInjectable;
@@ -3,27 +3,31 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import { getBoolean } from "../../../utils/parse-query";
import { contentTypes } from "../../../router/router-content-types";
import { clusterRoute } from "../../../router/route";
import getHelmReleaseValuesInjectable from "../../../helm/helm-service/get-helm-release-values.injectable";
const getReleaseRouteValuesInjectable = getRouteInjectable({
id: "get-release-values-route",
instantiate: () => clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace}/{release}/values`,
})(async ({ cluster, params: { namespace, release }, query }) => ({
response: await helmService.getReleaseValues(release, {
cluster,
namespace,
all: getBoolean(query, "all"),
}),
instantiate: (di) => {
const getHelmReleaseValues = di.inject(getHelmReleaseValuesInjectable);
contentType: contentTypes.txt,
})),
return clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace}/{release}/values`,
})(async ({ cluster, params: { namespace, release }, query }) => ({
response: await getHelmReleaseValues(release, {
cluster,
namespace,
all: getBoolean(query, "all"),
}),
contentType: contentTypes.txt,
}));
},
});
export default getReleaseRouteValuesInjectable;
@@ -3,11 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import type { InstallChartArgs } from "../../../helm/helm-service";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import Joi from "joi";
import { payloadValidatedClusterRoute } from "../../../router/route";
import type { InstallChartArgs } from "../../../helm/helm-service/install-helm-chart.injectable";
import installHelmChartInjectable from "../../../helm/helm-service/install-helm-chart.injectable";
const installChartArgsValidator = Joi.object<InstallChartArgs, true, InstallChartArgs>({
chart: Joi
@@ -31,14 +31,18 @@ const installChartArgsValidator = Joi.object<InstallChartArgs, true, InstallChar
const installChartRouteInjectable = getRouteInjectable({
id: "install-chart-route",
instantiate: () => payloadValidatedClusterRoute({
method: "post",
path: `${apiPrefix}/v2/releases`,
payloadValidator: installChartArgsValidator,
})(async ({ payload, cluster }) => ({
response: await helmService.installChart(cluster, payload),
statusCode: 201,
})),
instantiate: (di) => {
const installHelmChart = di.inject(installHelmChartInjectable);
return payloadValidatedClusterRoute({
method: "post",
path: `${apiPrefix}/v2/releases`,
payloadValidator: installChartArgsValidator,
})(async ({ payload, cluster }) => ({
response: await installHelmChart(cluster, payload),
statusCode: 201,
}));
},
});
export default installChartRouteInjectable;
@@ -3,19 +3,23 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import { clusterRoute } from "../../../router/route";
import listHelmReleasesInjectable from "../../../helm/helm-service/list-helm-releases.injectable";
const listReleasesRouteInjectable = getRouteInjectable({
id: "list-releases-route",
instantiate: () => clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace?}`,
})(async ({ cluster, params }) => ({
response: await helmService.listReleases(cluster, params.namespace),
})),
instantiate: (di) => {
const listHelmReleases = di.inject(listHelmReleasesInjectable);
return clusterRoute({
method: "get",
path: `${apiPrefix}/v2/releases/{namespace?}`,
})(async ({ cluster, params }) => ({
response: await listHelmReleases(cluster, params.namespace),
}));
},
});
export default listReleasesRouteInjectable;
@@ -3,10 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import Joi from "joi";
import { payloadValidatedClusterRoute } from "../../../router/route";
import rollbackHelmReleaseInjectable from "../../../helm/helm-service/rollback-helm-release.injectable";
interface RollbackReleasePayload {
revision: number;
@@ -21,13 +21,17 @@ const rollbackReleasePayloadValidator = Joi.object<RollbackReleasePayload, true,
const rollbackReleaseRouteInjectable = getRouteInjectable({
id: "rollback-release-route",
instantiate: () => payloadValidatedClusterRoute({
method: "put",
path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback`,
payloadValidator: rollbackReleasePayloadValidator,
})(async ({ cluster, params: { release, namespace }, payload }) => {
await helmService.rollback(cluster, release, namespace, payload.revision);
}),
instantiate: (di) => {
const rollbackRelease = di.inject(rollbackHelmReleaseInjectable);
return payloadValidatedClusterRoute({
method: "put",
path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback`,
payloadValidator: rollbackReleasePayloadValidator,
})(async ({ cluster, params: { release, namespace }, payload }) => {
await rollbackRelease(cluster, release, namespace, payload.revision);
});
},
});
export default rollbackReleaseRouteInjectable;
@@ -3,11 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { apiPrefix } from "../../../../common/vars";
import type { UpdateChartArgs } from "../../../helm/helm-service";
import { helmService } from "../../../helm/helm-service";
import { getRouteInjectable } from "../../../router/router.injectable";
import { payloadValidatedClusterRoute } from "../../../router/route";
import Joi from "joi";
import type { UpdateChartArgs } from "../../../helm/helm-service/update-helm-release.injectable";
import updateHelmReleaseInjectable from "../../../helm/helm-service/update-helm-release.injectable";
const updateChartArgsValidator = Joi.object<UpdateChartArgs, true, UpdateChartArgs>({
chart: Joi
@@ -24,18 +24,22 @@ const updateChartArgsValidator = Joi.object<UpdateChartArgs, true, UpdateChartAr
const updateReleaseRouteInjectable = getRouteInjectable({
id: "update-release-route",
instantiate: () => payloadValidatedClusterRoute({
method: "put",
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
payloadValidator: updateChartArgsValidator,
})(async ({ cluster, params, payload }) => ({
response: await helmService.updateRelease(
cluster,
params.release,
params.namespace,
payload,
),
})),
instantiate: (di) => {
const updateRelease = di.inject(updateHelmReleaseInjectable);
return payloadValidatedClusterRoute({
method: "put",
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
payloadValidator: updateChartArgsValidator,
})(async ({ cluster, params, payload }) => ({
response: await updateRelease(
cluster,
params.release,
params.namespace,
payload,
),
}));
},
});
export default updateReleaseRouteInjectable;