mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
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:
parent
5420780ae0
commit
1393cc303d
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,387 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import { fireEvent } from "@testing-library/react";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import execFileInjectable from "../../common/fs/exec-file.injectable";
|
||||||
|
import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectable";
|
||||||
|
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
|
import type { HelmRepo } from "../../common/helm/helm-repo";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
import isPathInjectable from "../../renderer/components/input/validators/is-path.injectable";
|
||||||
|
import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable";
|
||||||
|
import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable";
|
||||||
|
import type { AsyncResult } from "../../common/utils/async-result";
|
||||||
|
|
||||||
|
// TODO: Make tooltips free of side effects by making it deterministic
|
||||||
|
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
||||||
|
withTooltip: (target: any) => target,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("add custom helm repository in preferences", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let showSuccessNotificationMock: jest.Mock;
|
||||||
|
let showErrorNotificationMock: jest.Mock;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let execFileMock: AsyncFnMock<
|
||||||
|
ReturnType<typeof execFileInjectable["instantiate"]>
|
||||||
|
>;
|
||||||
|
let getActiveHelmRepositoriesMock: AsyncFnMock<() => AsyncResult<HelmRepo[]>>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.useFakeTimers("modern");
|
||||||
|
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
execFileMock = asyncFn();
|
||||||
|
getActiveHelmRepositoriesMock = asyncFn();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
rendererDi.override(callForPublicHelmRepositoriesInjectable, () => async () => []);
|
||||||
|
|
||||||
|
showSuccessNotificationMock = jest.fn();
|
||||||
|
|
||||||
|
rendererDi.override(showSuccessNotificationInjectable, () => showSuccessNotificationMock);
|
||||||
|
|
||||||
|
showErrorNotificationMock = jest.fn();
|
||||||
|
|
||||||
|
rendererDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
|
||||||
|
|
||||||
|
// TODO: Figure out how to make async validators unit testable
|
||||||
|
rendererDi.override(isPathInjectable, () => ({ debounce: 0, validate: async () => {} }));
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
getActiveHelmRepositoriesInjectable,
|
||||||
|
() => getActiveHelmRepositoriesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(execFileInjectable, () => execFileMock);
|
||||||
|
mainDi.override(helmBinaryPathInjectable, () => "some-helm-binary-path");
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to preferences containing helm repositories", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.preferences.navigate();
|
||||||
|
applicationBuilder.preferences.navigation.click("kubernetes");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when active repositories resolve", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
getActiveHelmRepositoriesMock.resolve({
|
||||||
|
callWasSuccessful: true,
|
||||||
|
response: [
|
||||||
|
{ name: "Some active repository", url: "some-url" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when selecting to add custom repository", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("add-custom-helm-repo-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows dialog", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("add-custom-helm-repository-dialog"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Figure out how to close dialog by clicking outside of it
|
||||||
|
xdescribe("when closing the dialog by clicking outside", () => {
|
||||||
|
beforeEach(() => {});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("add-custom-helm-repository-dialog"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when closing the dialog by clicking cancel", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("custom-helm-repository-cancel-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("add-custom-helm-repository-dialog"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when inputted minimal options for the repository", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
getActiveHelmRepositoriesMock.mockClear();
|
||||||
|
|
||||||
|
const nameInput = rendered.getByTestId("custom-helm-repository-name-input");
|
||||||
|
|
||||||
|
fireEvent.change(nameInput, { target: { value: "some-custom-repository" }});
|
||||||
|
|
||||||
|
const urlInput = rendered.getByTestId("custom-helm-repository-url-input");
|
||||||
|
|
||||||
|
fireEvent.change(urlInput, { target: { value: "http://some.url" }});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when submitted and some time passes", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const submitButton = rendered.getByTestId("custom-helm-repository-submit-button");
|
||||||
|
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
// TODO: Remove when debounce is removed from WizardStep.submit
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds the repository", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "some-custom-repository", "http://some.url"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories yet", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show notification yet", () => {
|
||||||
|
expect(showSuccessNotificationMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when activation rejects", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.reject(
|
||||||
|
"Some error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Some error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show success notification", () => {
|
||||||
|
expect(showSuccessNotificationMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(rendered.queryByTestId("add-custom-helm-repository-dialog")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when activation resolves with success", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
[
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "some-custom-repository", "http://some.url"],
|
||||||
|
],
|
||||||
|
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(rendered.queryByTestId("add-custom-helm-repository-dialog")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reloads active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows success notification", () => {
|
||||||
|
expect(showSuccessNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Helm repository some-custom-repository has been added.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when adding custom repository again", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("add-custom-helm-repo-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("repository name is empty", () => {
|
||||||
|
const input = rendered.getByTestId("custom-helm-repository-name-input") as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(input.value).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("repository url is empty", () => {
|
||||||
|
const input = rendered.getByTestId("custom-helm-repository-url-input") as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(input.value).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when showing the maximal options", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("toggle-maximal-options-for-custom-helm-repository-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows maximal options", () => {
|
||||||
|
const maximalOptions = rendered.getByTestId("maximal-options-for-custom-helm-repository-dialog");
|
||||||
|
|
||||||
|
expect(maximalOptions).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given closing the dialog, when reopening the dialog, still shows maximal options", () => {
|
||||||
|
const cancelButton = rendered.getByTestId("custom-helm-repository-cancel-button");
|
||||||
|
|
||||||
|
fireEvent.click(cancelButton);
|
||||||
|
|
||||||
|
const openButton = rendered.getByTestId("add-custom-helm-repo-button");
|
||||||
|
|
||||||
|
fireEvent.click(openButton);
|
||||||
|
|
||||||
|
const maximalOptions = rendered.getByTestId("maximal-options-for-custom-helm-repository-dialog");
|
||||||
|
|
||||||
|
expect(maximalOptions).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when hiding maximal options", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("toggle-maximal-options-for-custom-helm-repository-button");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show maximal options anymore", () => {
|
||||||
|
const maximalOptions = rendered.queryByTestId("maximal-options-for-custom-helm-repository-dialog");
|
||||||
|
|
||||||
|
expect(maximalOptions).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when inputted maximal options", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
[
|
||||||
|
{ selector: "username-input", value: "some-username" },
|
||||||
|
{ selector: "password-input", value: "some-password" },
|
||||||
|
{ selector: "ca-cert-file-input", value: "some-ca-cert-file" },
|
||||||
|
{ selector: "cert-file-input", value: "some-cert-file" },
|
||||||
|
{ selector: "key-file-input", value: "some-key-file" },
|
||||||
|
].forEach(({ selector, value }) => {
|
||||||
|
const input = rendered.getByTestId(`custom-helm-repository-${selector}`);
|
||||||
|
|
||||||
|
fireEvent.change(input, { target: { value }});
|
||||||
|
});
|
||||||
|
|
||||||
|
const checkbox = rendered.getByTestId(`custom-helm-repository-verify-tls-input`);
|
||||||
|
|
||||||
|
fireEvent.click(checkbox);
|
||||||
|
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when submitted and some time passes, adds the repository with maximal options", () => {
|
||||||
|
const submitButton = rendered.getByTestId("custom-helm-repository-submit-button");
|
||||||
|
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
// TODO: Remove when debounce is removed from WizardStep.submit
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
[
|
||||||
|
"repo",
|
||||||
|
"add",
|
||||||
|
"some-custom-repository",
|
||||||
|
"http://some.url",
|
||||||
|
"--insecure-skip-tls-verify",
|
||||||
|
"--username",
|
||||||
|
"some-username",
|
||||||
|
"--password",
|
||||||
|
"some-password",
|
||||||
|
"--ca-file",
|
||||||
|
"some-ca-cert-file",
|
||||||
|
"--key-file",
|
||||||
|
"some-key-file",
|
||||||
|
"--cert-file",
|
||||||
|
"some-cert-file",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,278 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import execFileInjectable from "../../common/fs/exec-file.injectable";
|
||||||
|
import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectable";
|
||||||
|
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
|
import type { HelmRepo } from "../../common/helm/helm-repo";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable";
|
||||||
|
import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable";
|
||||||
|
import type { AsyncResult } from "../../common/utils/async-result";
|
||||||
|
|
||||||
|
// TODO: Make tooltips free of side effects by making it deterministic
|
||||||
|
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
||||||
|
withTooltip: (target: any) => target,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("add helm repository from list in preferences", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let showSuccessNotificationMock: jest.Mock;
|
||||||
|
let showErrorNotificationMock: jest.Mock;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let execFileMock: AsyncFnMock<
|
||||||
|
ReturnType<typeof execFileInjectable["instantiate"]>
|
||||||
|
>;
|
||||||
|
let getActiveHelmRepositoriesMock: AsyncFnMock<() => AsyncResult<HelmRepo[]>>;
|
||||||
|
let callForPublicHelmRepositoriesMock: AsyncFnMock<() => Promise<HelmRepo[]>>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
execFileMock = asyncFn();
|
||||||
|
getActiveHelmRepositoriesMock = asyncFn();
|
||||||
|
callForPublicHelmRepositoriesMock = asyncFn();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
showSuccessNotificationMock = jest.fn();
|
||||||
|
|
||||||
|
rendererDi.override(showSuccessNotificationInjectable, () => showSuccessNotificationMock);
|
||||||
|
|
||||||
|
showErrorNotificationMock = jest.fn();
|
||||||
|
|
||||||
|
rendererDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
|
||||||
|
|
||||||
|
rendererDi.override(
|
||||||
|
callForPublicHelmRepositoriesInjectable,
|
||||||
|
() => callForPublicHelmRepositoriesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
getActiveHelmRepositoriesInjectable,
|
||||||
|
() => getActiveHelmRepositoriesMock,
|
||||||
|
);
|
||||||
|
mainDi.override(execFileInjectable, () => execFileMock);
|
||||||
|
mainDi.override(helmBinaryPathInjectable, () => "some-helm-binary-path");
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to preferences containing helm repositories", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.preferences.navigate();
|
||||||
|
applicationBuilder.preferences.navigation.click("kubernetes");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls for public repositories", () => {
|
||||||
|
expect(callForPublicHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls for active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when both active and public repositories resolve", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
callForPublicHelmRepositoriesMock.resolve([
|
||||||
|
{ name: "Some already active repository", url: "some-url" },
|
||||||
|
{ name: "Some to be added repository", url: "some-other-url" },
|
||||||
|
]),
|
||||||
|
|
||||||
|
getActiveHelmRepositoriesMock.resolve({
|
||||||
|
callWasSuccessful: true,
|
||||||
|
response: [
|
||||||
|
{ name: "Some already active repository", url: "some-url" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when select for adding public repositories is clicked", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder.select.openMenu(
|
||||||
|
"selection-of-active-public-helm-repository",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when deactive public repository is selected", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
getActiveHelmRepositoriesMock.mockClear();
|
||||||
|
|
||||||
|
applicationBuilder.select.selectOption(
|
||||||
|
"selection-of-active-public-helm-repository",
|
||||||
|
"Some to be added repository",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds the repository", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "Some to be added repository", "some-other-url"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories yet", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when adding rejects", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.reject(
|
||||||
|
"Some error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Some error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show success notification", () => {
|
||||||
|
expect(showSuccessNotificationMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show dialog anymore", () => {
|
||||||
|
expect(rendered.queryByTestId("add-custom-helm-repository-dialog")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when adding resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
[
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "Some to be added repository", "some-other-url"],
|
||||||
|
],
|
||||||
|
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reloads active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows success notification", () => {
|
||||||
|
expect(showSuccessNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Helm repository Some to be added repository has been added.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when active repositories resolve again", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await getActiveHelmRepositoriesMock.resolve({
|
||||||
|
callWasSuccessful: true,
|
||||||
|
response: [
|
||||||
|
{ name: "Some already active repository", url: "some-url" },
|
||||||
|
{ name: "Some to be added repository", url: "some-other-url" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when select for selecting active repositories is clicked", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder.select.openMenu(
|
||||||
|
"selection-of-active-public-helm-repository",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when active repository is selected", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
getActiveHelmRepositoriesMock.mockClear();
|
||||||
|
|
||||||
|
applicationBuilder.select.selectOption(
|
||||||
|
"selection-of-active-public-helm-repository",
|
||||||
|
"Some already active repository",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes the repository", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "remove", "Some already active repository"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories yet", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when removing resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
[
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "remove", "Some already active repository"],
|
||||||
|
],
|
||||||
|
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reloads active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,458 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { ReadYamlFile } from "../../common/fs/read-yaml-file.injectable";
|
||||||
|
import readYamlFileInjectable from "../../common/fs/read-yaml-file.injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import type { HelmRepositoriesFromYaml } from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
|
import execFileInjectable from "../../common/fs/exec-file.injectable";
|
||||||
|
import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectable";
|
||||||
|
import loggerInjectable from "../../common/logger.injectable";
|
||||||
|
import type { Logger } from "../../common/logger";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable";
|
||||||
|
|
||||||
|
// TODO: Make tooltips free of side effects by making it deterministic
|
||||||
|
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
||||||
|
withTooltip: (target: any) => target,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("listing active helm repositories in preferences", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let readYamlFileMock: AsyncFnMock<ReadYamlFile>;
|
||||||
|
let execFileMock: AsyncFnMock<ReturnType<typeof execFileInjectable["instantiate"]>>;
|
||||||
|
let loggerStub: Logger;
|
||||||
|
let showErrorNotificationMock: jest.Mock;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
readYamlFileMock = asyncFn();
|
||||||
|
execFileMock = asyncFn();
|
||||||
|
|
||||||
|
loggerStub = { error: jest.fn() } as unknown as Logger;
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
showErrorNotificationMock = jest.fn();
|
||||||
|
|
||||||
|
rendererDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
|
||||||
|
rendererDi.override(callForPublicHelmRepositoriesInjectable, () => async () => []);
|
||||||
|
mainDi.override(readYamlFileInjectable, () => readYamlFileMock);
|
||||||
|
mainDi.override(execFileInjectable, () => execFileMock);
|
||||||
|
mainDi.override(helmBinaryPathInjectable, () => "some-helm-binary-path");
|
||||||
|
mainDi.override(loggerInjectable, () => loggerStub);
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to preferences containing helm repositories", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.preferences.navigate();
|
||||||
|
applicationBuilder.preferences.navigation.click("kubernetes");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows loader for repositories", () => {
|
||||||
|
expect(
|
||||||
|
rendered.getByTestId("helm-repositories-are-loading"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls for helm configuration", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["env"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not call for updating of repositories yet", () => {
|
||||||
|
expect(execFileMock).not.toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "update"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when getting configuration rejects", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.reject("some-error");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Error getting Helm configuration: some-error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes all helm controls", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-controls"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when configuration resolves without path to repository config file", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
["some-helm-binary-path", ["env"]],
|
||||||
|
"HELM_REPOSITORY_CACHE=some-helm-repository-cache-path",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs error", () => {
|
||||||
|
expect(loggerStub.error).toHaveBeenCalledWith(
|
||||||
|
"Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Error getting Helm configuration: Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes all helm controls", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-controls"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when configuration resolves without path to repository cache directory", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
["some-helm-binary-path", ["env"]],
|
||||||
|
"HELM_REPOSITORY_CONFIG=some-helm-repository-config-file.yaml",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs error", () => {
|
||||||
|
expect(loggerStub.error).toHaveBeenCalledWith(
|
||||||
|
"Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Error getting Helm configuration: Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes all helm controls", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-controls"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when configuration resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
["some-helm-binary-path", ["env"]],
|
||||||
|
|
||||||
|
[
|
||||||
|
"HELM_REPOSITORY_CONFIG=some-helm-repository-config-file.yaml",
|
||||||
|
"HELM_REPOSITORY_CACHE=some-helm-repository-cache-path",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls for update of repositories", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "update"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not call for repositories yet", () => {
|
||||||
|
expect(readYamlFileMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when updating repositories reject with any other error", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.reject("Some error");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Error updating Helm repositories: Some error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes all helm controls", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-controls"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when updating repositories reject with error about no existing repositories", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
|
||||||
|
await execFileMock.reject(
|
||||||
|
"Error: no repositories found. You must add one before updating",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still shows the loader for repositories", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds "bitnami" as default repository', () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "bitnami", "https://charts.bitnami.com/bitnami"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when adding default repository reject", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.reject("Some error");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error notification", () => {
|
||||||
|
expect(showErrorNotificationMock).toHaveBeenCalledWith(
|
||||||
|
"Error when adding default Helm repository: Some error",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes all helm controls", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-controls"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when adding of default repository resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
readYamlFileMock.mockClear();
|
||||||
|
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
[
|
||||||
|
"some-helm-binary-path",
|
||||||
|
|
||||||
|
[
|
||||||
|
"repo",
|
||||||
|
"add",
|
||||||
|
"bitnami",
|
||||||
|
"https://charts.bitnami.com/bitnami",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still shows the loader for repositories", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls for repositories again", () => {
|
||||||
|
expect(readYamlFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-repository-config-file.yaml",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when another call for repositories resolve", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await readYamlFileMock.resolveSpecific(
|
||||||
|
["some-helm-repository-config-file.yaml"],
|
||||||
|
|
||||||
|
{
|
||||||
|
repositories: [
|
||||||
|
{
|
||||||
|
name: "bitnami",
|
||||||
|
url: "https://charts.bitnami.com/bitnami",
|
||||||
|
caFile: "irrelevant",
|
||||||
|
certFile: "irrelevant",
|
||||||
|
insecure_skip_tls_verify: false,
|
||||||
|
keyFile: "irrelevant",
|
||||||
|
pass_credentials_all: false,
|
||||||
|
password: "irrelevant",
|
||||||
|
username: "irrelevant",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows the added repository", () => {
|
||||||
|
const actual = rendered.getByTestId("helm-repository-bitnami");
|
||||||
|
|
||||||
|
expect(actual).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when updating repositories resolve", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
["some-helm-binary-path", ["repo", "update"]],
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads repositories from file system", () => {
|
||||||
|
expect(readYamlFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-repository-config-file.yaml",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when repositories resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
|
||||||
|
await readYamlFileMock.resolveSpecific(
|
||||||
|
["some-helm-repository-config-file.yaml"],
|
||||||
|
repositoryConfigStub,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not add default repository", () => {
|
||||||
|
expect(execFileMock).not.toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "add", "bitnami", "https://charts.bitnami.com/bitnami"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show loader for repositories anymore", () => {
|
||||||
|
expect(
|
||||||
|
rendered.queryByTestId("helm-repositories-are-loading"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows repositories in use", () => {
|
||||||
|
const actual = rendered.getAllByTestId(
|
||||||
|
/^helm-repository-(some-repository|some-other-repository)$/,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(actual).toHaveLength(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const repositoryConfigStub: HelmRepositoriesFromYaml = {
|
||||||
|
repositories: [
|
||||||
|
{
|
||||||
|
name: "some-repository",
|
||||||
|
url: "some-repository-url",
|
||||||
|
caFile: "irrelevant",
|
||||||
|
certFile: "irrelevant",
|
||||||
|
insecure_skip_tls_verify: false,
|
||||||
|
keyFile: "irrelevant",
|
||||||
|
pass_credentials_all: false,
|
||||||
|
password: "irrelevant",
|
||||||
|
username: "irrelevant",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "some-other-repository",
|
||||||
|
url: "some-other-repository-url",
|
||||||
|
caFile: "irrelevant",
|
||||||
|
certFile: "irrelevant",
|
||||||
|
insecure_skip_tls_verify: false,
|
||||||
|
keyFile: "irrelevant",
|
||||||
|
pass_credentials_all: false,
|
||||||
|
password: "irrelevant",
|
||||||
|
username: "irrelevant",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import { fireEvent } from "@testing-library/react";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import execFileInjectable from "../../common/fs/exec-file.injectable";
|
||||||
|
import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectable";
|
||||||
|
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
|
import type { HelmRepo } from "../../common/helm/helm-repo";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
import type { AsyncResult } from "../../common/utils/async-result";
|
||||||
|
|
||||||
|
// TODO: Make tooltips free of side effects by making it deterministic
|
||||||
|
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
||||||
|
withTooltip: (target: any) => target,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("remove helm repository from list of active repositories in preferences", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let getActiveHelmRepositoriesMock: AsyncFnMock<() => AsyncResult<HelmRepo[]>>;
|
||||||
|
let execFileMock: AsyncFnMock<
|
||||||
|
ReturnType<typeof execFileInjectable["instantiate"]>
|
||||||
|
>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
execFileMock = asyncFn();
|
||||||
|
getActiveHelmRepositoriesMock = asyncFn();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
rendererDi.override(callForPublicHelmRepositoriesInjectable, () => async () => []);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
getActiveHelmRepositoriesInjectable,
|
||||||
|
() => getActiveHelmRepositoriesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(execFileInjectable, () => execFileMock);
|
||||||
|
mainDi.override(helmBinaryPathInjectable, () => "some-helm-binary-path");
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to preferences containing helm repositories", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.preferences.navigate();
|
||||||
|
applicationBuilder.preferences.navigation.click("kubernetes");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when active repositories resolve", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
getActiveHelmRepositoriesMock.resolve({
|
||||||
|
callWasSuccessful: true,
|
||||||
|
response: [
|
||||||
|
{ name: "some-active-repository", url: "some-url" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when removing repository", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
execFileMock.mockClear();
|
||||||
|
getActiveHelmRepositoriesMock.mockClear();
|
||||||
|
|
||||||
|
const removeButton = rendered.getByTestId(
|
||||||
|
"remove-helm-repository-some-active-repository",
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(removeButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes the repository", () => {
|
||||||
|
expect(execFileMock).toHaveBeenCalledWith(
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "remove", "some-active-repository"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not reload active repositories yet", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when removing resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await execFileMock.resolveSpecific(
|
||||||
|
[
|
||||||
|
"some-helm-binary-path",
|
||||||
|
["repo", "remove", "some-active-repository"],
|
||||||
|
],
|
||||||
|
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reloads active repositories", () => {
|
||||||
|
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -833,6 +833,9 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
Helm Charts
|
Helm Charts
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
|
<div
|
||||||
|
data-testid="helm-controls"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="flex gaps"
|
class="flex gaps"
|
||||||
>
|
>
|
||||||
@ -841,7 +844,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-HelmRepoSelect-live-region"
|
id="react-select-selection-of-active-public-helm-repository-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -857,7 +860,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-HelmRepoSelect-placeholder"
|
id="react-select-selection-of-active-public-helm-repository-placeholder"
|
||||||
>
|
>
|
||||||
Repositories
|
Repositories
|
||||||
</div>
|
</div>
|
||||||
@ -867,7 +870,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-HelmRepoSelect-placeholder"
|
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -875,7 +878,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
class="Select__input"
|
class="Select__input"
|
||||||
disabled=""
|
disabled=""
|
||||||
id="HelmRepoSelect"
|
id="selection-of-active-public-helm-repository"
|
||||||
role="combobox"
|
role="combobox"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||||
@ -927,6 +930,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
class="Button primary"
|
class="Button primary"
|
||||||
|
data-testid="add-custom-helm-repo-button"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
Add Custom Helm Repo
|
Add Custom Helm Repo
|
||||||
@ -940,9 +944,12 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Spinner singleColor center"
|
class="Spinner singleColor center"
|
||||||
|
data-testid="helm-repositories-are-loading"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation to kubernetes preferences", () => {
|
describe("preferences - navigation to kubernetes preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -17,6 +19,15 @@ describe("preferences - navigation to kubernetes preferences", () => {
|
|||||||
let rendered: RenderResult;
|
let rendered: RenderResult;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ rendererDi, mainDi }) => {
|
||||||
|
rendererDi.override(callForPublicHelmRepositoriesInjectable, () => async () => []);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
getActiveHelmRepositoriesInjectable,
|
||||||
|
() => async () => ({ callWasSuccessful: true, response: [] }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
applicationBuilder.beforeRender(() => {
|
applicationBuilder.beforeRender(() => {
|
||||||
applicationBuilder.preferences.navigate();
|
applicationBuilder.preferences.navigate();
|
||||||
});
|
});
|
||||||
|
|||||||
25
src/common/fs/exec-file.injectable.ts
Normal file
25
src/common/fs/exec-file.injectable.ts
Normal file
@ -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 { execFile } from "child_process";
|
||||||
|
import { promisify } from "util";
|
||||||
|
|
||||||
|
export type ExecFile = (filePath: string, args: string[]) => Promise<string>;
|
||||||
|
|
||||||
|
const execFileInjectable = getInjectable({
|
||||||
|
id: "exec-file",
|
||||||
|
|
||||||
|
instantiate: (): ExecFile => async (filePath, args) => {
|
||||||
|
const asyncExecFile = promisify(execFile);
|
||||||
|
|
||||||
|
const result = await asyncExecFile(filePath, args);
|
||||||
|
|
||||||
|
return result.stdout;
|
||||||
|
},
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default execFileInjectable;
|
||||||
25
src/common/fs/read-yaml-file.injectable.ts
Normal file
25
src/common/fs/read-yaml-file.injectable.ts
Normal file
@ -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 readFileInjectable from "./read-file.injectable";
|
||||||
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
|
export type ReadYamlFile = (filePath: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
const readYamlFileInjectable = getInjectable({
|
||||||
|
id: "read-yaml-file",
|
||||||
|
|
||||||
|
instantiate: (di): ReadYamlFile => {
|
||||||
|
const readFile = di.inject(readFileInjectable);
|
||||||
|
|
||||||
|
return async (filePath: string) => {
|
||||||
|
const contents = await readFile(filePath);
|
||||||
|
|
||||||
|
return yaml.load(contents);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default readYamlFileInjectable;
|
||||||
23
src/common/helm/add-helm-repository-channel.injectable.ts
Normal file
23
src/common/helm/add-helm-repository-channel.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 "./helm-repo";
|
||||||
|
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
|
||||||
|
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
|
||||||
|
import type { AsyncResult } from "../utils/async-result";
|
||||||
|
|
||||||
|
export type AddHelmRepositoryChannel = RequestChannel<HelmRepo, AsyncResult<string>>;
|
||||||
|
|
||||||
|
const addHelmRepositoryChannelInjectable = getInjectable({
|
||||||
|
id: "add-helm-repository-channel",
|
||||||
|
|
||||||
|
instantiate: (): AddHelmRepositoryChannel => ({
|
||||||
|
id: "add-helm-repository-channel",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: requestChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default addHelmRepositoryChannelInjectable;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 { RequestChannel } from "../utils/channel/request-channel-injection-token";
|
||||||
|
import type { HelmRepo } from "./helm-repo";
|
||||||
|
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
|
||||||
|
import type { AsyncResult } from "../utils/async-result";
|
||||||
|
|
||||||
|
export type GetHelmRepositoriesChannel = RequestChannel<void, AsyncResult<HelmRepo[]>>;
|
||||||
|
|
||||||
|
const getActiveHelmRepositoriesChannelInjectable = getInjectable({
|
||||||
|
id: "get-active-helm-repositories-channel",
|
||||||
|
|
||||||
|
instantiate: (): GetHelmRepositoriesChannel => ({
|
||||||
|
id: "get-helm-active-list-repositories",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: requestChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getActiveHelmRepositoriesChannelInjectable;
|
||||||
16
src/common/helm/helm-repo.ts
Normal file
16
src/common/helm/helm-repo.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||||
|
export type HelmRepo = {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
cacheFilePath?: string;
|
||||||
|
caFile?: string;
|
||||||
|
certFile?: string;
|
||||||
|
insecureSkipTlsVerify?: boolean;
|
||||||
|
keyFile?: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
};
|
||||||
22
src/common/helm/remove-helm-repository-channel.injectable.ts
Normal file
22
src/common/helm/remove-helm-repository-channel.injectable.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { HelmRepo } from "./helm-repo";
|
||||||
|
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
|
||||||
|
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
|
||||||
|
|
||||||
|
export type RemoveHelmRepositoryChannel = RequestChannel<HelmRepo>;
|
||||||
|
|
||||||
|
const removeHelmRepositoryChannelInjectable = getInjectable({
|
||||||
|
id: "remove-helm-repository-channel",
|
||||||
|
|
||||||
|
instantiate: (): RemoveHelmRepositoryChannel => ({
|
||||||
|
id: "remove-helm-repository-channel",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: requestChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default removeHelmRepositoryChannelInjectable;
|
||||||
7
src/common/utils/async-result.ts
Normal file
7
src/common/utils/async-result.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
export type AsyncResult<Response, Error = string> =
|
||||||
|
| { callWasSuccessful: true; response: Response }
|
||||||
|
| { callWasSuccessful: false; error: Error };
|
||||||
15
src/common/utils/get-error-message.ts
Normal file
15
src/common/utils/get-error-message.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
export const getErrorMessage = (error: unknown): string => {
|
||||||
|
if (typeof error === "string") {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.stringify(error);
|
||||||
|
};
|
||||||
@ -3,16 +3,22 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import path from "path";
|
|
||||||
import bundledBinariesNormalizedArchInjectable from "./bundled-binaries-normalized-arch.injectable";
|
|
||||||
import bundledResourcesDirectoryInjectable from "./bundled-resources-dir.injectable";
|
import bundledResourcesDirectoryInjectable from "./bundled-resources-dir.injectable";
|
||||||
|
import getAbsolutePathInjectable from "../path/get-absolute-path.injectable";
|
||||||
|
import normalizedPlatformArchitectureInjectable from "./normalized-platform-architecture.injectable";
|
||||||
|
|
||||||
const baseBundeledBinariesDirectoryInjectable = getInjectable({
|
const baseBundledBinariesDirectoryInjectable = getInjectable({
|
||||||
id: "base-bundeled-binaries-directory",
|
id: "base-bundled-binaries-directory",
|
||||||
instantiate: (di) => path.join(
|
instantiate: (di) => {
|
||||||
di.inject(bundledResourcesDirectoryInjectable),
|
const bundledResourcesDirectory = di.inject(bundledResourcesDirectoryInjectable);
|
||||||
di.inject(bundledBinariesNormalizedArchInjectable),
|
const normalizedPlatformArchitecture = di.inject(normalizedPlatformArchitectureInjectable);
|
||||||
),
|
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
||||||
|
|
||||||
|
return getAbsolutePath(
|
||||||
|
bundledResourcesDirectory,
|
||||||
|
normalizedPlatformArchitecture,
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default baseBundeledBinariesDirectoryInjectable;
|
export default baseBundledBinariesDirectoryInjectable;
|
||||||
|
|||||||
@ -3,19 +3,22 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import path from "path";
|
|
||||||
import isProductionInjectable from "./is-production.injectable";
|
import isProductionInjectable from "./is-production.injectable";
|
||||||
import normalizedPlatformInjectable from "./normalized-platform.injectable";
|
import normalizedPlatformInjectable from "./normalized-platform.injectable";
|
||||||
|
import getAbsolutePathInjectable from "../path/get-absolute-path.injectable";
|
||||||
|
import lensResourcesDirInjectable from "./lens-resources-dir.injectable";
|
||||||
|
|
||||||
const bundledResourcesDirectoryInjectable = getInjectable({
|
const bundledResourcesDirectoryInjectable = getInjectable({
|
||||||
id: "bundled-resources-directory",
|
id: "bundled-resources-directory",
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const isProduction = di.inject(isProductionInjectable);
|
const isProduction = di.inject(isProductionInjectable);
|
||||||
const normalizedPlatform = di.inject(normalizedPlatformInjectable);
|
const normalizedPlatform = di.inject(normalizedPlatformInjectable);
|
||||||
|
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
||||||
|
const lensResourcesDir = di.inject(lensResourcesDirInjectable);
|
||||||
|
|
||||||
return isProduction
|
return isProduction
|
||||||
? process.resourcesPath
|
? lensResourcesDir
|
||||||
: path.join(process.cwd(), "binaries", "client", normalizedPlatform);
|
: getAbsolutePath(lensResourcesDir, "binaries", "client", normalizedPlatform);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
const bundledBinariesNormalizedArchInjectable = getInjectable({
|
const normalizedPlatformArchitectureInjectable = getInjectable({
|
||||||
id: "bundled-binaries-normalized-arch",
|
id: "normalized-platform-architecture",
|
||||||
instantiate: () => {
|
instantiate: () => {
|
||||||
switch (process.arch) {
|
switch (process.arch) {
|
||||||
case "arm64":
|
case "arm64":
|
||||||
@ -24,4 +24,4 @@ const bundledBinariesNormalizedArchInjectable = getInjectable({
|
|||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default bundledBinariesNormalizedArchInjectable;
|
export default normalizedPlatformArchitectureInjectable;
|
||||||
@ -3,11 +3,15 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import platformInjectable from "./platform.injectable";
|
||||||
|
|
||||||
const normalizedPlatformInjectable = getInjectable({
|
const normalizedPlatformInjectable = getInjectable({
|
||||||
id: "normalized-platform",
|
id: "normalized-platform",
|
||||||
instantiate: () => {
|
|
||||||
switch (process.platform) {
|
instantiate: (di) => {
|
||||||
|
const platform = di.inject(platformInjectable);
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return "darwin";
|
return "darwin";
|
||||||
case "linux":
|
case "linux":
|
||||||
@ -15,10 +19,9 @@ const normalizedPlatformInjectable = getInjectable({
|
|||||||
case "win32":
|
case "win32":
|
||||||
return "windows";
|
return "windows";
|
||||||
default:
|
default:
|
||||||
throw new Error(`platform=${process.platform} is unsupported`);
|
throw new Error(`platform=${platform} is unsupported`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
causesSideEffects: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default normalizedPlatformInjectable;
|
export default normalizedPlatformInjectable;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import glob from "glob";
|
import glob from "glob";
|
||||||
import { kebabCase, memoize, noop } from "lodash/fp";
|
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 { createContainer } from "@ogre-tools/injectable";
|
||||||
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
import appNameInjectable from "./app-paths/app-name/app-name.injectable";
|
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 electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable";
|
||||||
import publishIsConfiguredInjectable from "./application-update/publish-is-configured.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 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 setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-quit.injectable";
|
||||||
import downloadPlatformUpdateInjectable from "./application-update/download-platform-update/download-platform-update.injectable";
|
import downloadPlatformUpdateInjectable from "./application-update/download-platform-update/download-platform-update.injectable";
|
||||||
import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.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 appVersionInjectable from "../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||||
import getRandomIdInjectable from "../common/utils/get-random-id.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 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 } = {}) {
|
export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) {
|
||||||
const {
|
const {
|
||||||
@ -134,6 +147,24 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
|
|
||||||
di.override(periodicalCheckForUpdatesInjectable, () => ({ start: () => {}, stop: () => {}, started: false }));
|
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
|
// TODO: Remove usages of globally exported appEventBus to get rid of this
|
||||||
di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>());
|
di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>());
|
||||||
|
|
||||||
@ -141,7 +172,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
di.override(broadcastMessageInjectable, () => (channel) => {
|
di.override(broadcastMessageInjectable, () => (channel) => {
|
||||||
throw new Error(`Tried to broadcast message to channel "${channel}" over IPC without explicit override.`);
|
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, () => () => {
|
di.override(spawnInjectable, () => () => {
|
||||||
return {
|
return {
|
||||||
stderr: { on: jest.fn(), removeAllListeners: jest.fn() },
|
stderr: { on: jest.fn(), removeAllListeners: jest.fn() },
|
||||||
@ -150,18 +181,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
} as never;
|
} 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, () => ({
|
di.override(loggerInjectable, () => ({
|
||||||
warn: noop,
|
warn: noop,
|
||||||
debug: noop,
|
debug: noop,
|
||||||
@ -204,6 +223,7 @@ const overrideOperatingSystem = (di: DiContainer) => {
|
|||||||
di.override(platformInjectable, () => "darwin");
|
di.override(platformInjectable, () => "darwin");
|
||||||
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
|
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
|
||||||
di.override(joinPathsInjectable, () => joinPathsFake);
|
di.override(joinPathsInjectable, () => joinPathsFake);
|
||||||
|
di.override(normalizedPlatformArchitectureInjectable, () => "arm64");
|
||||||
};
|
};
|
||||||
|
|
||||||
const overrideElectronFeatures = (di: DiContainer) => {
|
const overrideElectronFeatures = (di: DiContainer) => {
|
||||||
@ -257,3 +277,11 @@ const overrideElectronFeatures = (di: DiContainer) => {
|
|||||||
di.override(publishIsConfiguredInjectable, () => false);
|
di.override(publishIsConfiguredInjectable, () => false);
|
||||||
di.override(electronUpdaterIsActiveInjectable, () => 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 { sortCharts } from "../../../common/utils";
|
||||||
import type { HelmRepo } from "../helm-repo-manager";
|
import type { HelmRepo } from "../../../common/helm/helm-repo";
|
||||||
|
|
||||||
const charts = new Map([
|
const charts = new Map([
|
||||||
["stable", {
|
["stable", {
|
||||||
|
|||||||
@ -3,27 +3,48 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { helmService } from "../helm-service";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import { HelmRepoManager } from "../helm-repo-manager";
|
import listHelmChartsInjectable from "../helm-service/list-helm-charts.injectable";
|
||||||
|
import getActiveHelmRepositoriesInjectable from "../repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
|
||||||
const mockHelmRepoManager = jest.spyOn(HelmRepoManager, "getInstance").mockImplementation();
|
import type { AsyncResult } from "../../../common/utils/async-result";
|
||||||
|
import type { HelmRepo } from "../../../common/helm/helm-repo";
|
||||||
|
|
||||||
jest.mock("../helm-chart-manager");
|
jest.mock("../helm-chart-manager");
|
||||||
|
|
||||||
describe("Helm Service tests", () => {
|
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(() => {
|
afterEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("list charts with deprecated entries", async () => {
|
it("list charts with deprecated entries", async () => {
|
||||||
mockHelmRepoManager.mockReturnValue({
|
getActiveHelmRepositoriesMock.mockReturnValue(
|
||||||
repositories: jest.fn().mockImplementation(async () => [
|
Promise.resolve({
|
||||||
|
callWasSuccessful: true,
|
||||||
|
|
||||||
|
response: [
|
||||||
{ name: "stable", url: "stableurl" },
|
{ name: "stable", url: "stableurl" },
|
||||||
{ name: "experiment", url: "experimenturl" },
|
{ name: "experiment", url: "experimenturl" },
|
||||||
]),
|
],
|
||||||
} as any);
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const charts = await helmService.listCharts();
|
const charts = await listHelmCharts();
|
||||||
|
|
||||||
expect(charts).toEqual({
|
expect(charts).toEqual({
|
||||||
stable: {
|
stable: {
|
||||||
@ -123,15 +144,14 @@ describe("Helm Service tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("list charts sorted by version in descending order", async () => {
|
it("list charts sorted by version in descending order", async () => {
|
||||||
mockHelmRepoManager.mockReturnValue({
|
getActiveHelmRepositoriesMock.mockReturnValue(
|
||||||
repositories: jest.fn().mockImplementation(async () => {
|
Promise.resolve({
|
||||||
return [
|
callWasSuccessful: true,
|
||||||
{ name: "bitnami", url: "bitnamiurl" },
|
response: [{ name: "bitnami", url: "bitnamiurl" }],
|
||||||
];
|
|
||||||
}),
|
}),
|
||||||
} as any);
|
);
|
||||||
|
|
||||||
const charts = await helmService.listCharts();
|
const charts = await listHelmCharts();
|
||||||
|
|
||||||
expect(charts).toEqual({
|
expect(charts).toEqual({
|
||||||
bitnami: {
|
bitnami: {
|
||||||
|
|||||||
31
src/main/helm/exec-helm/exec-helm.injectable.ts
Normal file
31
src/main/helm/exec-helm/exec-helm.injectable.ts
Normal file
@ -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;
|
||||||
43
src/main/helm/get-helm-env/get-helm-env.injectable.ts
Normal file
43
src/main/helm/get-helm-env/get-helm-env.injectable.ts
Normal file
@ -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;
|
||||||
25
src/main/helm/helm-binary-path.injectable.ts
Normal file
25
src/main/helm/helm-binary-path.injectable.ts
Normal file
@ -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;
|
||||||
@ -5,13 +5,13 @@
|
|||||||
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
import type { HelmRepo } from "./helm-repo-manager";
|
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api";
|
import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api";
|
||||||
import { iter, put, sortCharts } from "../../common/utils";
|
import { iter, put, sortCharts } from "../../common/utils";
|
||||||
import { execHelm } from "./exec";
|
import { execHelm } from "./exec";
|
||||||
import type { SetRequired } from "type-fest";
|
import type { SetRequired } from "type-fest";
|
||||||
import { assert } from "console";
|
import { assert } from "console";
|
||||||
|
import type { HelmRepo } from "../../common/helm/helm-repo";
|
||||||
|
|
||||||
interface ChartCacheEntry {
|
interface ChartCacheEntry {
|
||||||
data: string; // serialized JSON
|
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;
|
|
||||||
@ -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);
|
|
||||||
@ -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();
|
|
||||||
28
src/main/helm/helm-service/delete-helm-release.injectable.ts
Normal file
28
src/main/helm/helm-service/delete-helm-release.injectable.ts
Normal file
@ -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;
|
||||||
34
src/main/helm/helm-service/get-helm-chart.injectable.ts
Normal file
34
src/main/helm/helm-service/get-helm-chart.injectable.ts
Normal file
@ -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;
|
||||||
30
src/main/helm/helm-service/get-helm-release.injectable.ts
Normal file
30
src/main/helm/helm-service/get-helm-release.injectable.ts
Normal file
@ -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;
|
||||||
30
src/main/helm/helm-service/install-helm-chart.injectable.ts
Normal file
30
src/main/helm/helm-service/install-helm-chart.injectable.ts
Normal file
@ -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;
|
||||||
41
src/main/helm/helm-service/list-helm-charts.injectable.ts
Normal file
41
src/main/helm/helm-service/list-helm-charts.injectable.ts
Normal file
@ -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;
|
||||||
28
src/main/helm/helm-service/list-helm-releases.injectable.ts
Normal file
28
src/main/helm/helm-service/list-helm-releases.injectable.ts
Normal file
@ -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;
|
||||||
45
src/main/helm/helm-service/update-helm-release.injectable.ts
Normal file
45
src/main/helm/helm-service/update-helm-release.injectable.ts
Normal file
@ -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 spawnInjectable from "../child-process/spawn.injectable";
|
||||||
import { getKubeAuthProxyCertificate } from "./get-kube-auth-proxy-certificate";
|
import { getKubeAuthProxyCertificate } from "./get-kube-auth-proxy-certificate";
|
||||||
import loggerInjectable from "../../common/logger.injectable";
|
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;
|
export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy;
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ const createKubeAuthProxyInjectable = getInjectable({
|
|||||||
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => {
|
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => {
|
||||||
const clusterUrl = new URL(cluster.apiUrl);
|
const clusterUrl = new URL(cluster.apiUrl);
|
||||||
const dependencies: KubeAuthProxyDependencies = {
|
const dependencies: KubeAuthProxyDependencies = {
|
||||||
proxyBinPath: path.join(di.inject(baseBundeledBinariesDirectoryInjectable), binaryName),
|
proxyBinPath: path.join(di.inject(baseBundledBinariesDirectoryInjectable), binaryName),
|
||||||
proxyCert: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate),
|
proxyCert: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate),
|
||||||
spawn: di.inject(spawnInjectable),
|
spawn: di.inject(spawnInjectable),
|
||||||
logger: di.inject(loggerInjectable),
|
logger: di.inject(loggerInjectable),
|
||||||
|
|||||||
@ -4,13 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import path from "path";
|
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";
|
import kubectlBinaryNameInjectable from "./binary-name.injectable";
|
||||||
|
|
||||||
const bundledKubectlBinaryPathInjectable = getInjectable({
|
const bundledKubectlBinaryPathInjectable = getInjectable({
|
||||||
id: "bundled-kubectl-binary-path",
|
id: "bundled-kubectl-binary-path",
|
||||||
instantiate: (di) => path.join(
|
instantiate: (di) => path.join(
|
||||||
di.inject(baseBundeledBinariesDirectoryInjectable),
|
di.inject(baseBundledBinariesDirectoryInjectable),
|
||||||
di.inject(kubectlBinaryNameInjectable),
|
di.inject(kubectlBinaryNameInjectable),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import kubectlDownloadingNormalizedArchInjectable from "./normalized-arch.inject
|
|||||||
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
|
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
|
||||||
import kubectlBinaryNameInjectable from "./binary-name.injectable";
|
import kubectlBinaryNameInjectable from "./binary-name.injectable";
|
||||||
import bundledKubectlBinaryPathInjectable from "./bundled-binary-path.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({
|
const createKubectlInjectable = getInjectable({
|
||||||
id: "create-kubectl",
|
id: "create-kubectl",
|
||||||
@ -24,7 +24,7 @@ const createKubectlInjectable = getInjectable({
|
|||||||
normalizedDownloadPlatform: di.inject(normalizedPlatformInjectable),
|
normalizedDownloadPlatform: di.inject(normalizedPlatformInjectable),
|
||||||
kubectlBinaryName: di.inject(kubectlBinaryNameInjectable),
|
kubectlBinaryName: di.inject(kubectlBinaryNameInjectable),
|
||||||
bundledKubectlBinaryPath: di.inject(bundledKubectlBinaryPathInjectable),
|
bundledKubectlBinaryPath: di.inject(bundledKubectlBinaryPathInjectable),
|
||||||
baseBundeledBinariesDirectory: di.inject(baseBundeledBinariesDirectoryInjectable),
|
baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (clusterVersion: string) => new Kubectl(dependencies, clusterVersion);
|
return (clusterVersion: string) => new Kubectl(dependencies, clusterVersion);
|
||||||
|
|||||||
@ -5,25 +5,29 @@
|
|||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { route } from "../../../router/route";
|
import { route } from "../../../router/route";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
import getHelmChartInjectable from "../../../helm/helm-service/get-helm-chart.injectable";
|
||||||
|
|
||||||
const getChartRouteInjectable = getRouteInjectable({
|
const getChartRouteInjectable = getRouteInjectable({
|
||||||
id: "get-chart-route",
|
id: "get-chart-route",
|
||||||
|
|
||||||
instantiate: () => route({
|
instantiate: (di) => {
|
||||||
|
const getHelmChart = di.inject(getHelmChartInjectable);
|
||||||
|
|
||||||
|
return route({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/charts/{repo}/{chart}`,
|
path: `${apiPrefix}/v2/charts/{repo}/{chart}`,
|
||||||
})(async ({ params, query }) => {
|
})(async ({ params, query }) => {
|
||||||
const { repo, chart } = params;
|
const { repo, chart } = params;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: await helmService.getChart(
|
response: await getHelmChart(
|
||||||
repo,
|
repo,
|
||||||
chart,
|
chart,
|
||||||
query.get("version") ?? undefined,
|
query.get("version") ?? undefined,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default getChartRouteInjectable;
|
export default getChartRouteInjectable;
|
||||||
|
|||||||
@ -3,23 +3,27 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { route } from "../../../router/route";
|
import { route } from "../../../router/route";
|
||||||
|
import getHelmChartValuesInjectable from "../../../helm/helm-service/get-helm-chart-values.injectable";
|
||||||
|
|
||||||
const getChartRouteValuesInjectable = getRouteInjectable({
|
const getChartRouteValuesInjectable = getRouteInjectable({
|
||||||
id: "get-chart-route-values",
|
id: "get-chart-route-values",
|
||||||
|
|
||||||
instantiate: () => route({
|
instantiate: (di) => {
|
||||||
|
const getHelmChartValues = di.inject(getHelmChartValuesInjectable);
|
||||||
|
|
||||||
|
return route({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/charts/{repo}/{chart}/values`,
|
path: `${apiPrefix}/v2/charts/{repo}/{chart}/values`,
|
||||||
})(async ({ params, query }) => ({
|
})(async ({ params, query }) => ({
|
||||||
response: await helmService.getChartValues(
|
response: await getHelmChartValues(
|
||||||
params.repo,
|
params.repo,
|
||||||
params.chart,
|
params.chart,
|
||||||
query.get("version") ?? undefined,
|
query.get("version") ?? undefined,
|
||||||
),
|
),
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default getChartRouteValuesInjectable;
|
export default getChartRouteValuesInjectable;
|
||||||
|
|||||||
@ -3,19 +3,23 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { route } from "../../../router/route";
|
import { route } from "../../../router/route";
|
||||||
|
import listHelmChartsInjectable from "../../../helm/helm-service/list-helm-charts.injectable";
|
||||||
|
|
||||||
const listChartsRouteInjectable = getRouteInjectable({
|
const listChartsRouteInjectable = getRouteInjectable({
|
||||||
id: "list-charts-route",
|
id: "list-charts-route",
|
||||||
|
|
||||||
instantiate: () => route({
|
instantiate: (di) => {
|
||||||
|
const listHelmCharts = di.inject(listHelmChartsInjectable);
|
||||||
|
|
||||||
|
return route({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/charts`,
|
path: `${apiPrefix}/v2/charts`,
|
||||||
})(async () => ({
|
})(async () => ({
|
||||||
response: await helmService.listCharts(),
|
response: await listHelmCharts(),
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default listChartsRouteInjectable;
|
export default listChartsRouteInjectable;
|
||||||
|
|||||||
@ -3,23 +3,23 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { clusterRoute } from "../../../router/route";
|
import { clusterRoute } from "../../../router/route";
|
||||||
|
import deleteHelmReleaseInjectable from "../../../helm/helm-service/delete-helm-release.injectable";
|
||||||
|
|
||||||
const deleteReleaseRouteInjectable = getRouteInjectable({
|
const deleteReleaseRouteInjectable = getRouteInjectable({
|
||||||
id: "delete-release-route",
|
id: "delete-release-route",
|
||||||
|
|
||||||
instantiate: () => clusterRoute({
|
instantiate: (di) => {
|
||||||
|
const deleteHelmRelease = di.inject(deleteHelmReleaseInjectable);
|
||||||
|
|
||||||
|
return clusterRoute({
|
||||||
method: "delete",
|
method: "delete",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||||
})(async ({ cluster, params: { release, namespace }}) => ({
|
})(async ({ cluster, params: { release, namespace }}) => ({
|
||||||
response: await helmService.deleteRelease(
|
response: await deleteHelmRelease(cluster, release, namespace),
|
||||||
cluster,
|
}));
|
||||||
release,
|
},
|
||||||
namespace,
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default deleteReleaseRouteInjectable;
|
export default deleteReleaseRouteInjectable;
|
||||||
|
|||||||
@ -3,23 +3,27 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { clusterRoute } from "../../../router/route";
|
import { clusterRoute } from "../../../router/route";
|
||||||
|
import getHelmReleaseHistoryInjectable from "../../../helm/helm-service/get-helm-release-history.injectable";
|
||||||
|
|
||||||
const getReleaseRouteHistoryInjectable = getRouteInjectable({
|
const getReleaseRouteHistoryInjectable = getRouteInjectable({
|
||||||
id: "get-release-history-route",
|
id: "get-release-history-route",
|
||||||
|
|
||||||
instantiate: () => clusterRoute({
|
instantiate: (di) => {
|
||||||
|
const getHelmReleaseHistory = di.inject(getHelmReleaseHistoryInjectable);
|
||||||
|
|
||||||
|
return clusterRoute({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}/history`,
|
path: `${apiPrefix}/v2/releases/{namespace}/{release}/history`,
|
||||||
})(async ({ cluster, params }) => ({
|
})(async ({ cluster, params }) => ({
|
||||||
response: await helmService.getReleaseHistory(
|
response: await getHelmReleaseHistory(
|
||||||
cluster,
|
cluster,
|
||||||
params.release,
|
params.release,
|
||||||
params.namespace,
|
params.namespace,
|
||||||
),
|
),
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default getReleaseRouteHistoryInjectable;
|
export default getReleaseRouteHistoryInjectable;
|
||||||
|
|||||||
@ -3,23 +3,27 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { clusterRoute } from "../../../router/route";
|
import { clusterRoute } from "../../../router/route";
|
||||||
|
import getHelmReleaseInjectable from "../../../helm/helm-service/get-helm-release.injectable";
|
||||||
|
|
||||||
const getReleaseRouteInjectable = getRouteInjectable({
|
const getReleaseRouteInjectable = getRouteInjectable({
|
||||||
id: "get-release-route",
|
id: "get-release-route",
|
||||||
|
|
||||||
instantiate: () => clusterRoute({
|
instantiate: (di) => {
|
||||||
|
const getHelmRelease = di.inject(getHelmReleaseInjectable);
|
||||||
|
|
||||||
|
return clusterRoute({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||||
})(async ({ cluster, params }) => ({
|
})(async ({ cluster, params }) => ({
|
||||||
response: await helmService.getRelease(
|
response: await getHelmRelease(
|
||||||
cluster,
|
cluster,
|
||||||
params.release,
|
params.release,
|
||||||
params.namespace,
|
params.namespace,
|
||||||
),
|
),
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default getReleaseRouteInjectable;
|
export default getReleaseRouteInjectable;
|
||||||
|
|||||||
@ -3,27 +3,31 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { getBoolean } from "../../../utils/parse-query";
|
import { getBoolean } from "../../../utils/parse-query";
|
||||||
import { contentTypes } from "../../../router/router-content-types";
|
import { contentTypes } from "../../../router/router-content-types";
|
||||||
import { clusterRoute } from "../../../router/route";
|
import { clusterRoute } from "../../../router/route";
|
||||||
|
import getHelmReleaseValuesInjectable from "../../../helm/helm-service/get-helm-release-values.injectable";
|
||||||
|
|
||||||
const getReleaseRouteValuesInjectable = getRouteInjectable({
|
const getReleaseRouteValuesInjectable = getRouteInjectable({
|
||||||
id: "get-release-values-route",
|
id: "get-release-values-route",
|
||||||
|
|
||||||
instantiate: () => clusterRoute({
|
instantiate: (di) => {
|
||||||
|
const getHelmReleaseValues = di.inject(getHelmReleaseValuesInjectable);
|
||||||
|
|
||||||
|
return clusterRoute({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}/values`,
|
path: `${apiPrefix}/v2/releases/{namespace}/{release}/values`,
|
||||||
})(async ({ cluster, params: { namespace, release }, query }) => ({
|
})(async ({ cluster, params: { namespace, release }, query }) => ({
|
||||||
response: await helmService.getReleaseValues(release, {
|
response: await getHelmReleaseValues(release, {
|
||||||
cluster,
|
cluster,
|
||||||
namespace,
|
namespace,
|
||||||
all: getBoolean(query, "all"),
|
all: getBoolean(query, "all"),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
contentType: contentTypes.txt,
|
contentType: contentTypes.txt,
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default getReleaseRouteValuesInjectable;
|
export default getReleaseRouteValuesInjectable;
|
||||||
|
|||||||
@ -3,11 +3,11 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
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 { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { payloadValidatedClusterRoute } from "../../../router/route";
|
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>({
|
const installChartArgsValidator = Joi.object<InstallChartArgs, true, InstallChartArgs>({
|
||||||
chart: Joi
|
chart: Joi
|
||||||
@ -31,14 +31,18 @@ const installChartArgsValidator = Joi.object<InstallChartArgs, true, InstallChar
|
|||||||
const installChartRouteInjectable = getRouteInjectable({
|
const installChartRouteInjectable = getRouteInjectable({
|
||||||
id: "install-chart-route",
|
id: "install-chart-route",
|
||||||
|
|
||||||
instantiate: () => payloadValidatedClusterRoute({
|
instantiate: (di) => {
|
||||||
|
const installHelmChart = di.inject(installHelmChartInjectable);
|
||||||
|
|
||||||
|
return payloadValidatedClusterRoute({
|
||||||
method: "post",
|
method: "post",
|
||||||
path: `${apiPrefix}/v2/releases`,
|
path: `${apiPrefix}/v2/releases`,
|
||||||
payloadValidator: installChartArgsValidator,
|
payloadValidator: installChartArgsValidator,
|
||||||
})(async ({ payload, cluster }) => ({
|
})(async ({ payload, cluster }) => ({
|
||||||
response: await helmService.installChart(cluster, payload),
|
response: await installHelmChart(cluster, payload),
|
||||||
statusCode: 201,
|
statusCode: 201,
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default installChartRouteInjectable;
|
export default installChartRouteInjectable;
|
||||||
|
|||||||
@ -3,19 +3,23 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { clusterRoute } from "../../../router/route";
|
import { clusterRoute } from "../../../router/route";
|
||||||
|
import listHelmReleasesInjectable from "../../../helm/helm-service/list-helm-releases.injectable";
|
||||||
|
|
||||||
const listReleasesRouteInjectable = getRouteInjectable({
|
const listReleasesRouteInjectable = getRouteInjectable({
|
||||||
id: "list-releases-route",
|
id: "list-releases-route",
|
||||||
|
|
||||||
instantiate: () => clusterRoute({
|
instantiate: (di) => {
|
||||||
|
const listHelmReleases = di.inject(listHelmReleasesInjectable);
|
||||||
|
|
||||||
|
return clusterRoute({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace?}`,
|
path: `${apiPrefix}/v2/releases/{namespace?}`,
|
||||||
})(async ({ cluster, params }) => ({
|
})(async ({ cluster, params }) => ({
|
||||||
response: await helmService.listReleases(cluster, params.namespace),
|
response: await listHelmReleases(cluster, params.namespace),
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default listReleasesRouteInjectable;
|
export default listReleasesRouteInjectable;
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
import { apiPrefix } from "../../../../common/vars";
|
||||||
import { helmService } from "../../../helm/helm-service";
|
|
||||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { payloadValidatedClusterRoute } from "../../../router/route";
|
import { payloadValidatedClusterRoute } from "../../../router/route";
|
||||||
|
import rollbackHelmReleaseInjectable from "../../../helm/helm-service/rollback-helm-release.injectable";
|
||||||
|
|
||||||
interface RollbackReleasePayload {
|
interface RollbackReleasePayload {
|
||||||
revision: number;
|
revision: number;
|
||||||
@ -21,13 +21,17 @@ const rollbackReleasePayloadValidator = Joi.object<RollbackReleasePayload, true,
|
|||||||
const rollbackReleaseRouteInjectable = getRouteInjectable({
|
const rollbackReleaseRouteInjectable = getRouteInjectable({
|
||||||
id: "rollback-release-route",
|
id: "rollback-release-route",
|
||||||
|
|
||||||
instantiate: () => payloadValidatedClusterRoute({
|
instantiate: (di) => {
|
||||||
|
const rollbackRelease = di.inject(rollbackHelmReleaseInjectable);
|
||||||
|
|
||||||
|
return payloadValidatedClusterRoute({
|
||||||
method: "put",
|
method: "put",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback`,
|
path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback`,
|
||||||
payloadValidator: rollbackReleasePayloadValidator,
|
payloadValidator: rollbackReleasePayloadValidator,
|
||||||
})(async ({ cluster, params: { release, namespace }, payload }) => {
|
})(async ({ cluster, params: { release, namespace }, payload }) => {
|
||||||
await helmService.rollback(cluster, release, namespace, payload.revision);
|
await rollbackRelease(cluster, release, namespace, payload.revision);
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default rollbackReleaseRouteInjectable;
|
export default rollbackReleaseRouteInjectable;
|
||||||
|
|||||||
@ -3,11 +3,11 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { apiPrefix } from "../../../../common/vars";
|
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 { getRouteInjectable } from "../../../router/router.injectable";
|
||||||
import { payloadValidatedClusterRoute } from "../../../router/route";
|
import { payloadValidatedClusterRoute } from "../../../router/route";
|
||||||
import Joi from "joi";
|
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>({
|
const updateChartArgsValidator = Joi.object<UpdateChartArgs, true, UpdateChartArgs>({
|
||||||
chart: Joi
|
chart: Joi
|
||||||
@ -24,18 +24,22 @@ const updateChartArgsValidator = Joi.object<UpdateChartArgs, true, UpdateChartAr
|
|||||||
const updateReleaseRouteInjectable = getRouteInjectable({
|
const updateReleaseRouteInjectable = getRouteInjectable({
|
||||||
id: "update-release-route",
|
id: "update-release-route",
|
||||||
|
|
||||||
instantiate: () => payloadValidatedClusterRoute({
|
instantiate: (di) => {
|
||||||
|
const updateRelease = di.inject(updateHelmReleaseInjectable);
|
||||||
|
|
||||||
|
return payloadValidatedClusterRoute({
|
||||||
method: "put",
|
method: "put",
|
||||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||||
payloadValidator: updateChartArgsValidator,
|
payloadValidator: updateChartArgsValidator,
|
||||||
})(async ({ cluster, params, payload }) => ({
|
})(async ({ cluster, params, payload }) => ({
|
||||||
response: await helmService.updateRelease(
|
response: await updateRelease(
|
||||||
cluster,
|
cluster,
|
||||||
params.release,
|
params.release,
|
||||||
params.namespace,
|
params.namespace,
|
||||||
payload,
|
payload,
|
||||||
),
|
),
|
||||||
})),
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default updateReleaseRouteInjectable;
|
export default updateReleaseRouteInjectable;
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import * as LensExtensionsCommonApi from "../extensions/common-api";
|
|||||||
import * as LensExtensionsRendererApi from "../extensions/renderer-api";
|
import * as LensExtensionsRendererApi from "../extensions/renderer-api";
|
||||||
import { delay } from "../common/utils";
|
import { delay } from "../common/utils";
|
||||||
import { isMac, isDevelopment } from "../common/vars";
|
import { isMac, isDevelopment } from "../common/vars";
|
||||||
import { HelmRepoManager } from "../main/helm/helm-repo-manager";
|
|
||||||
import { DefaultProps } from "./mui-base-theme";
|
import { DefaultProps } from "./mui-base-theme";
|
||||||
import configurePackages from "../common/configure-packages";
|
import configurePackages from "../common/configure-packages";
|
||||||
import * as initializers from "./initializers";
|
import * as initializers from "./initializers";
|
||||||
@ -151,8 +150,6 @@ export async function bootstrap(di: DiContainer) {
|
|||||||
|
|
||||||
extensionInstallationStateStore.bindIpcListeners();
|
extensionInstallationStateStore.bindIpcListeners();
|
||||||
|
|
||||||
HelmRepoManager.createInstance(); // initialize the manager
|
|
||||||
|
|
||||||
// Register additional store listeners
|
// Register additional store listeners
|
||||||
clusterStore.registerIpcListener();
|
clusterStore.registerIpcListener();
|
||||||
|
|
||||||
|
|||||||
@ -1,220 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "./add-helm-repo-dialog.scss";
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import type { FileFilter } from "electron";
|
|
||||||
import { observable, makeObservable, action } from "mobx";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import type { DialogProps } from "../dialog";
|
|
||||||
import { Dialog } from "../dialog";
|
|
||||||
import { Wizard, WizardStep } from "../wizard";
|
|
||||||
import { Input } from "../input";
|
|
||||||
import { Checkbox } from "../checkbox";
|
|
||||||
import { Button } from "../button";
|
|
||||||
import { systemName, isUrl, isPath } from "../input/input_validators";
|
|
||||||
import { SubTitle } from "../layout/sub-title";
|
|
||||||
import { Icon } from "../icon";
|
|
||||||
import { Notifications } from "../notifications";
|
|
||||||
import { type HelmRepo, HelmRepoManager } from "../../../main/helm/helm-repo-manager";
|
|
||||||
import { requestOpenFilePickingDialog } from "../../ipc";
|
|
||||||
|
|
||||||
export interface AddHelmRepoDialogProps extends Partial<DialogProps> {
|
|
||||||
onAddRepo: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FileType {
|
|
||||||
CaFile = "caFile",
|
|
||||||
KeyFile = "keyFile",
|
|
||||||
CertFile = "certFile",
|
|
||||||
}
|
|
||||||
|
|
||||||
const dialogState = observable.object({
|
|
||||||
isOpen: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
function getEmptyRepo(): HelmRepo {
|
|
||||||
return { name: "", url: "", username: "", password: "", insecureSkipTlsVerify: false, caFile: "", keyFile: "", certFile: "" };
|
|
||||||
}
|
|
||||||
@observer
|
|
||||||
export class AddHelmRepoDialog extends React.Component<AddHelmRepoDialogProps> {
|
|
||||||
private static keyExtensions = ["key", "keystore", "jks", "p12", "pfx", "pem"];
|
|
||||||
private static certExtensions = ["crt", "cer", "ca-bundle", "p7b", "p7c", "p7s", "p12", "pfx", "pem"];
|
|
||||||
|
|
||||||
constructor(props: AddHelmRepoDialogProps) {
|
|
||||||
super(props);
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static open() {
|
|
||||||
dialogState.isOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static close() {
|
|
||||||
dialogState.isOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@observable helmRepo = getEmptyRepo();
|
|
||||||
@observable showOptions = false;
|
|
||||||
|
|
||||||
@action
|
|
||||||
close = () => {
|
|
||||||
AddHelmRepoDialog.close();
|
|
||||||
this.helmRepo = getEmptyRepo();
|
|
||||||
this.showOptions = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
setFilepath(type: FileType, value: string) {
|
|
||||||
this.helmRepo[type] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFilePath(type: FileType) {
|
|
||||||
return this.helmRepo[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
async selectFileDialog(type: FileType, fileFilter: FileFilter) {
|
|
||||||
const { canceled, filePaths } = await requestOpenFilePickingDialog({
|
|
||||||
defaultPath: this.getFilePath(type),
|
|
||||||
properties: ["openFile", "showHiddenFiles"],
|
|
||||||
message: `Select file`,
|
|
||||||
buttonLabel: `Use file`,
|
|
||||||
filters: [
|
|
||||||
fileFilter,
|
|
||||||
{ name: "Any", extensions: ["*"] },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!canceled && filePaths.length) {
|
|
||||||
this.setFilepath(type, filePaths[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addCustomRepo() {
|
|
||||||
try {
|
|
||||||
await HelmRepoManager.getInstance().addRepo(this.helmRepo);
|
|
||||||
Notifications.ok((
|
|
||||||
<>
|
|
||||||
{"Helm repository "}
|
|
||||||
<b>{this.helmRepo.name}</b>
|
|
||||||
{" has been added."}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
this.props.onAddRepo();
|
|
||||||
this.close();
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error((
|
|
||||||
<>
|
|
||||||
{"Adding helm branch "}
|
|
||||||
<b>{this.helmRepo.name}</b>
|
|
||||||
{" has failed: "}
|
|
||||||
{String(err)}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderFileInput(placeholder:string, fileType:FileType, fileExtensions:string[]){
|
|
||||||
return (
|
|
||||||
<div className="flex gaps align-center">
|
|
||||||
<Input
|
|
||||||
placeholder={placeholder}
|
|
||||||
validators={isPath}
|
|
||||||
className="box grow"
|
|
||||||
value={this.getFilePath(fileType)}
|
|
||||||
onChange={v => this.setFilepath(fileType, v)}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
material="folder"
|
|
||||||
onClick={() => this.selectFileDialog(fileType, { name: placeholder, extensions: fileExtensions })}
|
|
||||||
tooltip="Browse"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderOptions() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SubTitle title="Security settings" />
|
|
||||||
<Checkbox
|
|
||||||
label="Skip TLS certificate checks for the repository"
|
|
||||||
value={this.helmRepo.insecureSkipTlsVerify}
|
|
||||||
onChange={v => this.helmRepo.insecureSkipTlsVerify = v}
|
|
||||||
/>
|
|
||||||
{this.renderFileInput("Key file", FileType.KeyFile, AddHelmRepoDialog.keyExtensions)}
|
|
||||||
{this.renderFileInput("Ca file", FileType.CaFile, AddHelmRepoDialog.certExtensions)}
|
|
||||||
{this.renderFileInput("Certificate file", FileType.CertFile, AddHelmRepoDialog.certExtensions)}
|
|
||||||
<SubTitle title="Chart Repository Credentials" />
|
|
||||||
<Input
|
|
||||||
placeholder="Username"
|
|
||||||
value={this.helmRepo.username}
|
|
||||||
onChange= {v => this.helmRepo.username = v}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholder="Password"
|
|
||||||
value={this.helmRepo.password}
|
|
||||||
onChange={v => this.helmRepo.password = v}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { ...dialogProps } = this.props;
|
|
||||||
|
|
||||||
const header = <h5>Add custom Helm Repo</h5>;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
{...dialogProps}
|
|
||||||
className="AddHelmRepoDialog"
|
|
||||||
isOpen={dialogState.isOpen}
|
|
||||||
close={this.close}
|
|
||||||
>
|
|
||||||
<Wizard header={header} done={this.close}>
|
|
||||||
<WizardStep
|
|
||||||
contentClass="flow column"
|
|
||||||
nextLabel="Add"
|
|
||||||
next={() => this.addCustomRepo()}
|
|
||||||
>
|
|
||||||
<div className="flex column gaps">
|
|
||||||
<Input
|
|
||||||
autoFocus
|
|
||||||
required
|
|
||||||
placeholder="Helm repo name"
|
|
||||||
trim
|
|
||||||
validators={systemName}
|
|
||||||
value={this.helmRepo.name}
|
|
||||||
onChange={v => this.helmRepo.name = v}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
required
|
|
||||||
placeholder="URL"
|
|
||||||
validators={isUrl}
|
|
||||||
value={this.helmRepo.url}
|
|
||||||
onChange={v => this.helmRepo.url = v}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
plain
|
|
||||||
className="accordion"
|
|
||||||
onClick={() => this.showOptions = !this.showOptions}
|
|
||||||
>
|
|
||||||
More
|
|
||||||
<Icon
|
|
||||||
small
|
|
||||||
tooltip="More"
|
|
||||||
material={this.showOptions ? "remove" : "add"}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
{this.showOptions && this.renderOptions()}
|
|
||||||
</div>
|
|
||||||
</WizardStep>
|
|
||||||
</Wizard>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import styles from "./helm-charts.module.scss";
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { observable, makeObservable, computed } from "mobx";
|
|
||||||
|
|
||||||
import type { HelmRepo } from "../../../main/helm/helm-repo-manager";
|
|
||||||
import { HelmRepoManager } from "../../../main/helm/helm-repo-manager";
|
|
||||||
import { Button } from "../button";
|
|
||||||
import { Icon } from "../icon";
|
|
||||||
import { Notifications } from "../notifications";
|
|
||||||
import type { SelectOption } from "../select";
|
|
||||||
import { Select } from "../select";
|
|
||||||
import { AddHelmRepoDialog } from "./add-helm-repo-dialog";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { RemovableItem } from "./removable-item";
|
|
||||||
import { Notice } from "../+extensions/notice";
|
|
||||||
import { Spinner } from "../spinner";
|
|
||||||
import { noop } from "../../utils";
|
|
||||||
import type { SingleValue } from "react-select";
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class HelmCharts extends React.Component {
|
|
||||||
@observable loadingRepos = false;
|
|
||||||
@observable loadingAvailableRepos = false;
|
|
||||||
@observable repos: HelmRepo[] = [];
|
|
||||||
@observable addedRepos = observable.map<string, HelmRepo>();
|
|
||||||
|
|
||||||
constructor(props: {}) {
|
|
||||||
super(props);
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get repoOptions() {
|
|
||||||
return this.repos.map(repo => ({
|
|
||||||
value: repo,
|
|
||||||
label: repo.name,
|
|
||||||
isSelected: this.addedRepos.has(repo.name),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.loadAvailableRepos().catch(noop);
|
|
||||||
this.loadRepos().catch(noop);
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadAvailableRepos() {
|
|
||||||
this.loadingAvailableRepos = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!this.repos.length) {
|
|
||||||
this.repos = await HelmRepoManager.getInstance().loadAvailableRepos();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error(String(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingAvailableRepos = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadRepos() {
|
|
||||||
this.loadingRepos = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const repos = await HelmRepoManager.getInstance().repositories(); // via helm-cli
|
|
||||||
|
|
||||||
this.addedRepos.replace(repos.map(repo => [repo.name, repo]));
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error(String(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingRepos = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async addRepo(repo: HelmRepo) {
|
|
||||||
try {
|
|
||||||
await HelmRepoManager.getInstance().addRepo(repo);
|
|
||||||
this.addedRepos.set(repo.name, repo);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error((
|
|
||||||
<>
|
|
||||||
{"Adding helm branch "}
|
|
||||||
<b>{repo.name}</b>
|
|
||||||
{" has failed: "}
|
|
||||||
{String(err)}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeRepo(repo: HelmRepo) {
|
|
||||||
try {
|
|
||||||
await HelmRepoManager.getInstance().removeRepo(repo);
|
|
||||||
this.addedRepos.delete(repo.name);
|
|
||||||
} catch (err) {
|
|
||||||
Notifications.error(
|
|
||||||
<>
|
|
||||||
{"Removing helm branch "}
|
|
||||||
<b>{repo.name}</b>
|
|
||||||
{" has failed: "}
|
|
||||||
{String(err)}
|
|
||||||
</>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRepoSelect = async (option: SingleValue<{ value: HelmRepo }>): Promise<void> => {
|
|
||||||
if (!option) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.addedRepos.has(option.value.name)) {
|
|
||||||
return void Notifications.ok((
|
|
||||||
<>
|
|
||||||
{"Helm repo "}
|
|
||||||
<b>{option.value.name}</b>
|
|
||||||
{" already in use."}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.addRepo(option.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
formatOptionLabel = ({ value, isSelected }: SelectOption<HelmRepo>) => (
|
|
||||||
<div className="flex gaps">
|
|
||||||
<span>{value.name}</span>
|
|
||||||
{isSelected && (
|
|
||||||
<Icon
|
|
||||||
small
|
|
||||||
material="check"
|
|
||||||
className="box right" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
renderRepositories() {
|
|
||||||
const repos = Array.from(this.addedRepos);
|
|
||||||
|
|
||||||
if (this.loadingRepos) {
|
|
||||||
return <div className="pt-5 relative"><Spinner center/></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!repos.length) {
|
|
||||||
return (
|
|
||||||
<Notice>
|
|
||||||
<div className="flex-grow text-center">The repositories have not been added yet</div>
|
|
||||||
</Notice>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return repos.map(([name, repo]) => {
|
|
||||||
return (
|
|
||||||
<RemovableItem
|
|
||||||
key={name}
|
|
||||||
onRemove={() => this.removeRepo(repo)}
|
|
||||||
className="mt-3"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div data-testid="repository-name" className={styles.repoName}>{name}</div>
|
|
||||||
<div className={styles.repoUrl}>{repo.url}</div>
|
|
||||||
</div>
|
|
||||||
</RemovableItem>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex gaps">
|
|
||||||
<Select
|
|
||||||
id="HelmRepoSelect"
|
|
||||||
placeholder="Repositories"
|
|
||||||
isLoading={this.loadingAvailableRepos}
|
|
||||||
isDisabled={this.loadingAvailableRepos}
|
|
||||||
options={this.repoOptions}
|
|
||||||
onChange={this.onRepoSelect}
|
|
||||||
value={this.repos}
|
|
||||||
formatOptionLabel={this.formatOptionLabel}
|
|
||||||
controlShouldRenderValue={false}
|
|
||||||
className="box grow"
|
|
||||||
themeName="lens"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
primary
|
|
||||||
label="Add Custom Helm Repo"
|
|
||||||
onClick={AddHelmRepoDialog.open}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<AddHelmRepoDialog onAddRepo={() => this.loadRepos()}/>
|
|
||||||
<div className={styles.repos}>
|
|
||||||
{this.renderRepositories()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,8 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { HelmCharts } from "./kubernetes/helm-charts/helm-charts";
|
||||||
import { HelmCharts } from "./helm-charts";
|
|
||||||
import { KubeconfigSyncs } from "./kubeconfig-syncs";
|
import { KubeconfigSyncs } from "./kubeconfig-syncs";
|
||||||
import { KubectlBinaries } from "./kubectl-binaries";
|
import { KubectlBinaries } from "./kubectl-binaries";
|
||||||
import { Preferences } from "./preferences";
|
import { Preferences } from "./preferences";
|
||||||
|
|||||||
@ -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 { asyncComputed } from "@ogre-tools/injectable-react";
|
||||||
|
import getActiveHelmRepositoriesChannelInjectable from "../../../../../common/helm/get-active-helm-repositories-channel.injectable";
|
||||||
|
import { requestFromChannelInjectionToken } from "../../../../../common/utils/channel/request-from-channel-injection-token";
|
||||||
|
import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable";
|
||||||
|
import helmRepositoriesErrorStateInjectable from "./helm-repositories-error-state.injectable";
|
||||||
|
import { runInAction } from "mobx";
|
||||||
|
|
||||||
|
const activeHelmRepositoriesInjectable = getInjectable({
|
||||||
|
id: "active-helm-repositories",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||||
|
const getHelmRepositoriesChannel = di.inject(getActiveHelmRepositoriesChannelInjectable);
|
||||||
|
const showErrorNotification = di.inject(showErrorNotificationInjectable);
|
||||||
|
const helmRepositoriesErrorState = di.inject(helmRepositoriesErrorStateInjectable);
|
||||||
|
|
||||||
|
return asyncComputed(async () => {
|
||||||
|
const result = await requestFromChannel(getHelmRepositoriesChannel);
|
||||||
|
|
||||||
|
if (result.callWasSuccessful) {
|
||||||
|
return result.response;
|
||||||
|
} else {
|
||||||
|
showErrorNotification(result.error);
|
||||||
|
|
||||||
|
runInAction(() =>
|
||||||
|
helmRepositoriesErrorState.set({
|
||||||
|
controlsAreShown: false,
|
||||||
|
errorMessage: result.error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default activeHelmRepositoriesInjectable;
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./add-helm-repo-dialog.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Wizard, WizardStep } from "../../../../wizard";
|
||||||
|
import { Input } from "../../../../input";
|
||||||
|
import { systemName, isUrl } from "../../../../input/input_validators";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import customHelmRepoInjectable from "./custom-helm-repo.injectable";
|
||||||
|
import type { HelmRepo } from "../../../../../../common/helm/helm-repo";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import type { IObservableValue } from "mobx";
|
||||||
|
import { action } from "mobx";
|
||||||
|
import submitCustomHelmRepositoryInjectable from "./submit-custom-helm-repository.injectable";
|
||||||
|
import hideDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-adding-custom-helm-repository.injectable";
|
||||||
|
import { Button } from "../../../../button";
|
||||||
|
import { Icon } from "../../../../icon";
|
||||||
|
import maximalCustomHelmRepoOptionsAreShownInjectable from "./maximal-custom-helm-repo-options-are-shown.injectable";
|
||||||
|
import { SubTitle } from "../../../../layout/sub-title";
|
||||||
|
import { Checkbox } from "../../../../checkbox";
|
||||||
|
import { HelmFileInput } from "./helm-file-input/helm-file-input";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
helmRepo: HelmRepo;
|
||||||
|
hideDialog: () => void;
|
||||||
|
submitCustomRepository: (repository: HelmRepo) => Promise<void>;
|
||||||
|
maximalOptionsAreShown: IObservableValue<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActivationOfCustomHelmRepositoryDialogContent = observer(({ helmRepo, submitCustomRepository, maximalOptionsAreShown, hideDialog } : Dependencies) => (
|
||||||
|
<Wizard header={<h5>Add custom Helm Repo</h5>} done={hideDialog}>
|
||||||
|
<WizardStep
|
||||||
|
contentClass="flow column"
|
||||||
|
nextLabel="Add"
|
||||||
|
next={() => submitCustomRepository(helmRepo)}
|
||||||
|
testIdForNext="custom-helm-repository-submit-button"
|
||||||
|
testIdForPrev="custom-helm-repository-cancel-button"
|
||||||
|
>
|
||||||
|
<div className="flex column gaps" data-testid="add-custom-helm-repository-dialog">
|
||||||
|
<Input
|
||||||
|
autoFocus
|
||||||
|
required
|
||||||
|
placeholder="Helm repo name"
|
||||||
|
trim
|
||||||
|
validators={systemName}
|
||||||
|
value={helmRepo.name}
|
||||||
|
onChange={action(v => helmRepo.name = v)}
|
||||||
|
data-testid="custom-helm-repository-name-input"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
required
|
||||||
|
placeholder="URL"
|
||||||
|
validators={isUrl}
|
||||||
|
value={helmRepo.url}
|
||||||
|
onChange={action(v => helmRepo.url = v)}
|
||||||
|
data-testid="custom-helm-repository-url-input"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
plain
|
||||||
|
className="accordion"
|
||||||
|
data-testid="toggle-maximal-options-for-custom-helm-repository-button"
|
||||||
|
onClick={action(() => maximalOptionsAreShown.set(!maximalOptionsAreShown.get()))}
|
||||||
|
>
|
||||||
|
More
|
||||||
|
<Icon
|
||||||
|
small
|
||||||
|
tooltip="More"
|
||||||
|
material={maximalOptionsAreShown.get() ? "remove" : "add"}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{maximalOptionsAreShown.get() && (
|
||||||
|
<div data-testid="maximal-options-for-custom-helm-repository-dialog">
|
||||||
|
<SubTitle title="Security settings" />
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
label="Skip TLS certificate checks for the repository"
|
||||||
|
value={helmRepo.insecureSkipTlsVerify}
|
||||||
|
onChange={action(v => {
|
||||||
|
helmRepo.insecureSkipTlsVerify = v;
|
||||||
|
})}
|
||||||
|
data-testid="custom-helm-repository-verify-tls-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HelmFileInput
|
||||||
|
placeholder="Key file"
|
||||||
|
value={helmRepo.keyFile || ""}
|
||||||
|
setValue={action((value) => helmRepo.keyFile = value)}
|
||||||
|
fileExtensions={keyExtensions}
|
||||||
|
data-testid="custom-helm-repository-key-file-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HelmFileInput
|
||||||
|
placeholder="Ca file"
|
||||||
|
value={helmRepo.caFile || ""}
|
||||||
|
setValue={action((value) => helmRepo.caFile = value)}
|
||||||
|
fileExtensions={certExtensions}
|
||||||
|
data-testid="custom-helm-repository-ca-cert-file-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HelmFileInput
|
||||||
|
placeholder="Certificate file"
|
||||||
|
value={helmRepo.certFile || ""}
|
||||||
|
setValue={action((value) => helmRepo.certFile = value)}
|
||||||
|
fileExtensions={certExtensions}
|
||||||
|
data-testid="custom-helm-repository-cert-file-input"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SubTitle title="Chart Repository Credentials" />
|
||||||
|
|
||||||
|
<Input
|
||||||
|
placeholder="Username"
|
||||||
|
value={helmRepo.username}
|
||||||
|
onChange= {action(v => helmRepo.username = v)}
|
||||||
|
data-testid="custom-helm-repository-username-input"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={helmRepo.password}
|
||||||
|
onChange={action(v => helmRepo.password = v)}
|
||||||
|
data-testid="custom-helm-repository-password-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</WizardStep>
|
||||||
|
</Wizard>
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const AddingOfCustomHelmRepositoryDialogContent = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActivationOfCustomHelmRepositoryDialogContent,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
helmRepo: di.inject(customHelmRepoInjectable),
|
||||||
|
hideDialog: di.inject(hideDialogForAddingCustomHelmRepositoryInjectable),
|
||||||
|
submitCustomRepository: di.inject(submitCustomHelmRepositoryInjectable),
|
||||||
|
maximalOptionsAreShown: di.inject(maximalCustomHelmRepoOptionsAreShownInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const keyExtensions = ["key", "keystore", "jks", "p12", "pfx", "pem"];
|
||||||
|
const certExtensions = ["crt", "cer", "ca-bundle", "p7b", "p7c", "p7s", "p12", "pfx", "pem"];
|
||||||
|
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./add-helm-repo-dialog.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Dialog } from "../../../../dialog";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import { AddingOfCustomHelmRepositoryDialogContent } from "./adding-of-custom-helm-repository-dialog-content";
|
||||||
|
import addingOfCustomHelmRepositoryDialogIsVisibleInjectable from "./dialog-visibility/adding-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||||
|
import type { IObservableValue } from "mobx";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import hideDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-adding-custom-helm-repository.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
contentIsVisible: IObservableValue<boolean>;
|
||||||
|
hideDialog: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActivationOfCustomHelmRepositoryDialog = observer(({
|
||||||
|
contentIsVisible,
|
||||||
|
hideDialog,
|
||||||
|
}: Dependencies) => (
|
||||||
|
<div>
|
||||||
|
<Dialog
|
||||||
|
className="AddHelmRepoDialog"
|
||||||
|
isOpen={contentIsVisible.get()}
|
||||||
|
close={hideDialog}
|
||||||
|
>
|
||||||
|
{contentIsVisible.get() && <AddingOfCustomHelmRepositoryDialogContent />}
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
export const AddingOfCustomHelmRepositoryDialog = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActivationOfCustomHelmRepositoryDialog,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
contentIsVisible: di.inject(addingOfCustomHelmRepositoryDialogIsVisibleInjectable),
|
||||||
|
hideDialog: di.inject(hideDialogForAddingCustomHelmRepositoryInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import React from "react";
|
||||||
|
import { Button } from "../../../../button";
|
||||||
|
import showDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/show-dialog-for-adding-custom-helm-repository.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
showDialog: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActivationOfCustomHelmRepositoryOpenButton = ({ showDialog }: Dependencies) => (
|
||||||
|
<Button
|
||||||
|
primary
|
||||||
|
label="Add Custom Helm Repo"
|
||||||
|
onClick={showDialog}
|
||||||
|
data-testid="add-custom-helm-repo-button"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AddingOfCustomHelmRepositoryOpenButton = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActivationOfCustomHelmRepositoryOpenButton,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
showDialog: di.inject(showDialogForAddingCustomHelmRepositoryInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
@ -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, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
const customHelmRepoInjectable = getInjectable({
|
||||||
|
id: "custom-helm-repo",
|
||||||
|
|
||||||
|
instantiate: () => observable({
|
||||||
|
name: "",
|
||||||
|
url: "",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
insecureSkipTlsVerify: false,
|
||||||
|
caFile: "",
|
||||||
|
keyFile: "",
|
||||||
|
certFile: "",
|
||||||
|
}),
|
||||||
|
|
||||||
|
lifecycle: lifecycleEnum.transient,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default customHelmRepoInjectable;
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
const addingOfCustomHelmRepositoryDialogIsVisibleInjectable = getInjectable({
|
||||||
|
id: "adding-of-custom-helm-repository-dialog-is-visible",
|
||||||
|
instantiate: () => observable.box(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default addingOfCustomHelmRepositoryDialogIsVisibleInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { action } from "mobx";
|
||||||
|
import addingOfCustomHelmRepositoryDialogIsVisibleInjectable from "./adding-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||||
|
|
||||||
|
const hideDialogForAddingCustomHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "hide-dialog-for-adding-custom-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const state = di.inject(addingOfCustomHelmRepositoryDialogIsVisibleInjectable);
|
||||||
|
|
||||||
|
return action(() => {
|
||||||
|
state.set(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default hideDialogForAddingCustomHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { action } from "mobx";
|
||||||
|
import addingOfCustomHelmRepositoryDialogIsVisibleInjectable from "./adding-of-custom-helm-repository-dialog-is-visible.injectable";
|
||||||
|
|
||||||
|
const showDialogForAddingCustomHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "show-dialog-for-adding-custom-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const state = di.inject(addingOfCustomHelmRepositoryDialogIsVisibleInjectable);
|
||||||
|
|
||||||
|
return action(() => {
|
||||||
|
state.set(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default showDialogForAddingCustomHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 { FileFilter } from "electron";
|
||||||
|
import { requestOpenFilePickingDialog } from "../../../../../../ipc";
|
||||||
|
|
||||||
|
const getFilePathsInjectable = getInjectable({
|
||||||
|
id: "get-file-paths",
|
||||||
|
|
||||||
|
instantiate: () => async (fileFilter: FileFilter) =>
|
||||||
|
await requestOpenFilePickingDialog({
|
||||||
|
properties: ["openFile", "showHiddenFiles"],
|
||||||
|
message: `Select file`,
|
||||||
|
buttonLabel: `Use file`,
|
||||||
|
filters: [fileFilter, { name: "Any", extensions: ["*"] }],
|
||||||
|
}),
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getFilePathsInjectable;
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { InputValidator } from "../../../../../input";
|
||||||
|
import { Input } from "../../../../../input";
|
||||||
|
import { Icon } from "../../../../../icon";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import React from "react";
|
||||||
|
import getFilePathsInjectable from "./get-file-paths.injectable";
|
||||||
|
import type { FileFilter } from "electron";
|
||||||
|
import isPathInjectable from "../../../../../input/validators/is-path.injectable";
|
||||||
|
|
||||||
|
interface HelmFileInputProps {
|
||||||
|
placeholder: string;
|
||||||
|
fileExtensions: string[];
|
||||||
|
value: string;
|
||||||
|
setValue: (value: string) => void;
|
||||||
|
"data-testid"?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
getFilePaths: (fileFilter: FileFilter) => Promise<{ canceled: boolean; filePaths: string[] }>;
|
||||||
|
isPath: InputValidator<true>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedHelmFileInput = ({
|
||||||
|
placeholder,
|
||||||
|
value,
|
||||||
|
setValue,
|
||||||
|
fileExtensions,
|
||||||
|
getFilePaths,
|
||||||
|
isPath,
|
||||||
|
"data-testid": testId,
|
||||||
|
}: Dependencies & HelmFileInputProps) => (
|
||||||
|
<div className="flex gaps align-center">
|
||||||
|
<Input
|
||||||
|
placeholder={placeholder}
|
||||||
|
validators={isPath}
|
||||||
|
className="box grow"
|
||||||
|
value={value}
|
||||||
|
onChange={(v) => setValue(v)}
|
||||||
|
data-testid={testId}
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
material="folder"
|
||||||
|
|
||||||
|
onClick={async () => {
|
||||||
|
const { canceled, filePaths } = await getFilePaths({
|
||||||
|
name: placeholder,
|
||||||
|
extensions: fileExtensions,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!canceled && filePaths.length) {
|
||||||
|
setValue(filePaths[0]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
tooltip="Browse"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const HelmFileInput = withInjectables<Dependencies, HelmFileInputProps>(
|
||||||
|
NonInjectedHelmFileInput,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
getFilePaths: di.inject(getFilePathsInjectable),
|
||||||
|
isPath: di.inject(isPathInjectable),
|
||||||
|
...props,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
const maximalCustomHelmRepoOptionsAreShownInjectable = getInjectable({
|
||||||
|
id: "maximal-custom-helm-repo-options-are-shown",
|
||||||
|
instantiate: () => observable.box(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default maximalCustomHelmRepoOptionsAreShownInjectable;
|
||||||
@ -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 type { HelmRepo } from "../../../../../../common/helm/helm-repo";
|
||||||
|
import addHelmRepositoryInjectable from "../adding-of-public-helm-repository/select-helm-repository/add-helm-repository.injectable";
|
||||||
|
import hideDialogForAddingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-adding-custom-helm-repository.injectable";
|
||||||
|
|
||||||
|
const submitCustomHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "submit-custom-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const addHelmRepository = di.inject(addHelmRepositoryInjectable);
|
||||||
|
const hideDialog = di.inject(hideDialogForAddingCustomHelmRepositoryInjectable);
|
||||||
|
|
||||||
|
return async (repository: HelmRepo) => {
|
||||||
|
await addHelmRepository(repository);
|
||||||
|
|
||||||
|
hideDialog();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default submitCustomHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import React from "react";
|
||||||
|
import publicHelmRepositoriesInjectable from "./public-helm-repositories/public-helm-repositories.injectable";
|
||||||
|
import type { HelmRepo } from "../../../../../../common/helm/helm-repo";
|
||||||
|
import type { SelectOption } from "../../../../select";
|
||||||
|
import { Select } from "../../../../select";
|
||||||
|
import { Icon } from "../../../../icon";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import type { SingleValue } from "react-select";
|
||||||
|
import selectHelmRepositoryInjectable from "./select-helm-repository/select-helm-repository.injectable";
|
||||||
|
import { matches } from "lodash/fp";
|
||||||
|
import activeHelmRepositoriesInjectable from "../active-helm-repositories.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
publicRepositories: IAsyncComputed<HelmRepo[]>;
|
||||||
|
activeRepositories: IAsyncComputed<HelmRepo[]>;
|
||||||
|
selectRepository: (value: SingleValue<SelectOption<HelmRepo>>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedAddingOfPublicHelmRepository = observer(({
|
||||||
|
publicRepositories,
|
||||||
|
activeRepositories,
|
||||||
|
selectRepository,
|
||||||
|
}: Dependencies) => {
|
||||||
|
const dereferencesPublicRepositories = publicRepositories.value.get();
|
||||||
|
const dereferencesActiveRepositories = activeRepositories.value.get();
|
||||||
|
|
||||||
|
const valuesAreLoading = publicRepositories.pending.get() || activeRepositories.pending.get();
|
||||||
|
|
||||||
|
const repositoryOptions = dereferencesPublicRepositories.map(repository => ({
|
||||||
|
value: repository,
|
||||||
|
label: repository.name,
|
||||||
|
isSelected: !!dereferencesActiveRepositories.find(matches({ name: repository.name })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
id="selection-of-active-public-helm-repository"
|
||||||
|
placeholder="Repositories"
|
||||||
|
isLoading={valuesAreLoading}
|
||||||
|
isDisabled={valuesAreLoading}
|
||||||
|
options={repositoryOptions}
|
||||||
|
onChange={selectRepository}
|
||||||
|
value={dereferencesPublicRepositories}
|
||||||
|
formatOptionLabel={formatOptionLabel}
|
||||||
|
controlShouldRenderValue={false}
|
||||||
|
className="box grow"
|
||||||
|
themeName="lens"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AddingOfPublicHelmRepository = withInjectables<Dependencies>(
|
||||||
|
NonInjectedAddingOfPublicHelmRepository,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
publicRepositories: di.inject(publicHelmRepositoriesInjectable),
|
||||||
|
activeRepositories: di.inject(activeHelmRepositoriesInjectable),
|
||||||
|
selectRepository: di.inject(selectHelmRepositoryInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const formatOptionLabel = ({ value, isSelected }: SelectOption<HelmRepo>) => (
|
||||||
|
<div className="flex gaps">
|
||||||
|
<span>{value.name}</span>
|
||||||
|
{isSelected && (
|
||||||
|
<Icon
|
||||||
|
small
|
||||||
|
material="check"
|
||||||
|
className="box right" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
@ -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 { sortBy } from "lodash/fp";
|
||||||
|
import type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
|
||||||
|
import { customRequestPromise } from "../../../../../../../common/request";
|
||||||
|
|
||||||
|
const callForPublicHelmRepositoriesInjectable = getInjectable({
|
||||||
|
id: "call-for-public-helm-repositories",
|
||||||
|
|
||||||
|
instantiate: () => async (): 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,
|
||||||
|
});
|
||||||
|
|
||||||
|
const repositories = res.body as HelmRepo[];
|
||||||
|
|
||||||
|
return sortBy(repo => repo.name, repositories);
|
||||||
|
},
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default callForPublicHelmRepositoriesInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { asyncComputed } from "@ogre-tools/injectable-react";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "./call-for-public-helm-repositories.injectable";
|
||||||
|
|
||||||
|
const publicHelmRepositoriesInjectable = getInjectable({
|
||||||
|
id: "public-helm-repositories",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const callForPublicHelmRepositories = di.inject(callForPublicHelmRepositoriesInjectable);
|
||||||
|
|
||||||
|
return asyncComputed(async () => {
|
||||||
|
return await callForPublicHelmRepositories();
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default publicHelmRepositoriesInjectable;
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* 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 type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
|
||||||
|
import { requestFromChannelInjectionToken } from "../../../../../../../common/utils/channel/request-from-channel-injection-token";
|
||||||
|
import activeHelmRepositoriesInjectable from "../../active-helm-repositories.injectable";
|
||||||
|
import showErrorNotificationInjectable from "../../../../../notifications/show-error-notification.injectable";
|
||||||
|
import showSuccessNotificationInjectable from "../../../../../notifications/show-success-notification.injectable";
|
||||||
|
|
||||||
|
const addHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "add-public-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||||
|
const addHelmRepositoryChannel = di.inject(addHelmRepositoryChannelInjectable);
|
||||||
|
const activeHelmRepositories = di.inject(activeHelmRepositoriesInjectable);
|
||||||
|
const showErrorNotification = di.inject(showErrorNotificationInjectable);
|
||||||
|
const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
|
||||||
|
|
||||||
|
return async (repository: HelmRepo) => {
|
||||||
|
const result = await requestFromChannel(
|
||||||
|
addHelmRepositoryChannel,
|
||||||
|
repository,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.callWasSuccessful) {
|
||||||
|
showSuccessNotification(
|
||||||
|
`Helm repository ${repository.name} has been added.`,
|
||||||
|
);
|
||||||
|
|
||||||
|
activeHelmRepositories.invalidate();
|
||||||
|
} else {
|
||||||
|
showErrorNotification(result.error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default addHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 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 addHelmRepositoryInjectable from "./add-helm-repository.injectable";
|
||||||
|
import type { SelectOption } from "../../../../../select";
|
||||||
|
import type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
|
||||||
|
import type { SingleValue } from "react-select";
|
||||||
|
import removeHelmRepositoryInjectable from "../../remove-helm-repository.injectable";
|
||||||
|
|
||||||
|
const selectHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "select-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const addHelmRepository = di.inject(addHelmRepositoryInjectable);
|
||||||
|
const removeHelmRepository = di.inject(removeHelmRepositoryInjectable);
|
||||||
|
|
||||||
|
return (selected: SingleValue<SelectOption<HelmRepo>>) => {
|
||||||
|
if (!selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selected.isSelected) {
|
||||||
|
addHelmRepository(selected.value);
|
||||||
|
} else {
|
||||||
|
removeHelmRepository(selected.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default selectHelmRepositoryInjectable;
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { HelmRepositories } from "./helm-repositories";
|
||||||
|
import { AddingOfPublicHelmRepository } from "./adding-of-public-helm-repository/adding-of-public-helm-repository";
|
||||||
|
import { AddingOfCustomHelmRepositoryOpenButton } from "./adding-of-custom-helm-repository/adding-of-custom-helm-repository-open-button";
|
||||||
|
import { AddingOfCustomHelmRepositoryDialog } from "./adding-of-custom-helm-repository/adding-of-custom-helm-repository-dialog";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import type { HelmRepositoriesErrorState } from "./helm-repositories-error-state.injectable";
|
||||||
|
import helmRepositoriesErrorStateInjectable from "./helm-repositories-error-state.injectable";
|
||||||
|
import type { IObservableValue } from "mobx";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { Notice } from "../../../+extensions/notice";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
helmRepositoriesErrorState: IObservableValue<HelmRepositoriesErrorState>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedHelmCharts = observer(
|
||||||
|
({ helmRepositoriesErrorState }: Dependencies) => {
|
||||||
|
const state = helmRepositoriesErrorState.get();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{!state.controlsAreShown && (
|
||||||
|
<Notice>
|
||||||
|
<div className="flex-grow text-center">{state.errorMessage}</div>
|
||||||
|
</Notice>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state.controlsAreShown && (
|
||||||
|
<div data-testid="helm-controls">
|
||||||
|
<div className="flex gaps">
|
||||||
|
<AddingOfPublicHelmRepository />
|
||||||
|
|
||||||
|
<AddingOfCustomHelmRepositoryOpenButton />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<HelmRepositories />
|
||||||
|
|
||||||
|
<AddingOfCustomHelmRepositoryDialog />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const HelmCharts = withInjectables<Dependencies>(
|
||||||
|
NonInjectedHelmCharts,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
helmRepositoriesErrorState: di.inject(helmRepositoriesErrorStateInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
export type HelmRepositoriesErrorState =
|
||||||
|
| { controlsAreShown: true }
|
||||||
|
| { controlsAreShown: false; errorMessage: string };
|
||||||
|
|
||||||
|
const helmRepositoriesErrorStateInjectable = getInjectable({
|
||||||
|
id: "helm-repositories-error-state",
|
||||||
|
|
||||||
|
instantiate: () =>
|
||||||
|
observable.box<HelmRepositoriesErrorState>({ controlsAreShown: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default helmRepositoriesErrorStateInjectable;
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styles from "./helm-charts.module.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import activeHelmRepositoriesInjectable from "./active-helm-repositories.injectable";
|
||||||
|
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import { Spinner } from "../../../spinner";
|
||||||
|
import type { HelmRepo } from "../../../../../common/helm/helm-repo";
|
||||||
|
import { RemovableItem } from "../../removable-item";
|
||||||
|
import removeHelmRepositoryInjectable from "./remove-helm-repository.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
activeHelmRepositories: IAsyncComputed<HelmRepo[]>;
|
||||||
|
removeRepository: (repository: HelmRepo) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedActiveHelmRepositories = observer(({ activeHelmRepositories, removeRepository }: Dependencies) => {
|
||||||
|
if (activeHelmRepositories.pending.get()) {
|
||||||
|
return (
|
||||||
|
<div className={styles.repos}>
|
||||||
|
<div className="pt-5 relative">
|
||||||
|
<Spinner center data-testid="helm-repositories-are-loading" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const repositories = activeHelmRepositories.value.get();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.repos}>
|
||||||
|
{repositories.map((repository) => (
|
||||||
|
<RemovableItem
|
||||||
|
key={repository.name}
|
||||||
|
onRemove={() => removeRepository(repository)}
|
||||||
|
className="mt-3"
|
||||||
|
data-testid={`remove-helm-repository-${repository.name}`}
|
||||||
|
>
|
||||||
|
<div data-testid={`helm-repository-${repository.name}`} className={styles.repoName}>
|
||||||
|
{repository.name}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.repoUrl}>{repository.url}</div>
|
||||||
|
</RemovableItem>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export const HelmRepositories = withInjectables<Dependencies>(
|
||||||
|
NonInjectedActiveHelmRepositories,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
activeHelmRepositories: di.inject(activeHelmRepositoriesInjectable),
|
||||||
|
removeRepository: di.inject(removeHelmRepositoryInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
@ -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 type { HelmRepo } from "../../../../../common/helm/helm-repo";
|
||||||
|
import { requestFromChannelInjectionToken } from "../../../../../common/utils/channel/request-from-channel-injection-token";
|
||||||
|
import activeHelmRepositoriesInjectable from "./active-helm-repositories.injectable";
|
||||||
|
import removeHelmRepositoryChannelInjectable from "../../../../../common/helm/remove-helm-repository-channel.injectable";
|
||||||
|
|
||||||
|
const removePublicHelmRepositoryInjectable = getInjectable({
|
||||||
|
id: "remove-public-helm-repository",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||||
|
const removeHelmRepositoryChannel = di.inject(removeHelmRepositoryChannelInjectable);
|
||||||
|
const activeHelmRepositories = di.inject(activeHelmRepositoriesInjectable);
|
||||||
|
|
||||||
|
return async (repository: HelmRepo) => {
|
||||||
|
await requestFromChannel(removeHelmRepositoryChannel, repository);
|
||||||
|
|
||||||
|
activeHelmRepositories.invalidate();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default removePublicHelmRepositoryInjectable;
|
||||||
@ -14,9 +14,10 @@ export interface RemovableItemProps extends DOMAttributes<any>{
|
|||||||
icon?: string;
|
icon?: string;
|
||||||
onRemove: () => void;
|
onRemove: () => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
"data-testid"?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RemovableItem({ icon, onRemove, children, className, ...rest }: RemovableItemProps) {
|
export function RemovableItem({ icon, onRemove, children, className, "data-testid": testId, ...rest }: RemovableItemProps) {
|
||||||
return (
|
return (
|
||||||
<div className={cssNames(styles.item, "flex gaps align-center justify-space-between", className)} {...rest}>
|
<div className={cssNames(styles.item, "flex gaps align-center justify-space-between", className)} {...rest}>
|
||||||
{icon && (
|
{icon && (
|
||||||
@ -27,6 +28,7 @@ export function RemovableItem({ icon, onRemove, children, className, ...rest }:
|
|||||||
material="delete"
|
material="delete"
|
||||||
onClick={onRemove}
|
onClick={onRemove}
|
||||||
tooltip="Remove"
|
tooltip="Remove"
|
||||||
|
data-testid={testId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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 { AsyncInputValidationError, inputValidator } from "../input_validators";
|
||||||
|
import pathExistsInjectable from "../../../../common/fs/path-exists.injectable";
|
||||||
|
|
||||||
|
const isPathInjectable = getInjectable({
|
||||||
|
id: "is-path",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const pathExists = di.inject(pathExistsInjectable);
|
||||||
|
|
||||||
|
return inputValidator<true>({
|
||||||
|
debounce: 100,
|
||||||
|
condition: ({ type }) => type === "text",
|
||||||
|
|
||||||
|
validate: async (value) => {
|
||||||
|
try {
|
||||||
|
await pathExists(value);
|
||||||
|
} catch {
|
||||||
|
throw new AsyncInputValidationError(
|
||||||
|
`${value} is not a valid file path`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default isPathInjectable;
|
||||||
@ -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 type { NotificationMessage, Notification } from "./notifications.store";
|
||||||
|
import { NotificationStatus } from "./notifications.store";
|
||||||
|
import notificationsStoreInjectable from "./notifications-store.injectable";
|
||||||
|
|
||||||
|
const showErrorNotificationInjectable = getInjectable({
|
||||||
|
id: "show-error-notification",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const notificationsStore = di.inject(notificationsStoreInjectable);
|
||||||
|
|
||||||
|
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) =>
|
||||||
|
notificationsStore.add({
|
||||||
|
status: NotificationStatus.ERROR,
|
||||||
|
timeout: 5000,
|
||||||
|
message,
|
||||||
|
...customOpts,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default showErrorNotificationInjectable;
|
||||||
@ -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 type { NotificationMessage, Notification } from "./notifications.store";
|
||||||
|
import { NotificationStatus } from "./notifications.store";
|
||||||
|
import notificationsStoreInjectable from "./notifications-store.injectable";
|
||||||
|
|
||||||
|
const showSuccessNotificationInjectable = getInjectable({
|
||||||
|
id: "show-success-notification",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const notificationsStore = di.inject(notificationsStoreInjectable);
|
||||||
|
|
||||||
|
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) =>
|
||||||
|
notificationsStore.add({
|
||||||
|
status: NotificationStatus.OK,
|
||||||
|
timeout: 5000,
|
||||||
|
message,
|
||||||
|
...customOpts,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default showSuccessNotificationInjectable;
|
||||||
@ -233,7 +233,9 @@ class NonInjectedSelect<
|
|||||||
Menu: ({ className, ...props }) => (
|
Menu: ({ className, ...props }) => (
|
||||||
<WrappedMenu
|
<WrappedMenu
|
||||||
{...props}
|
{...props}
|
||||||
className={cssNames(menuClass, this.themeClass, className)}
|
className={cssNames(menuClass, this.themeClass, className, {
|
||||||
|
[`${inputId}-options`]: !!inputId,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { Observer } from "mobx-react";
|
|||||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||||
import allowedResourcesInjectable from "../../../common/cluster-store/allowed-resources.injectable";
|
import allowedResourcesInjectable from "../../../common/cluster-store/allowed-resources.injectable";
|
||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import { fireEvent } from "@testing-library/react";
|
import { getByText, fireEvent } from "@testing-library/react";
|
||||||
import type { KubeResource } from "../../../common/rbac";
|
import type { KubeResource } from "../../../common/rbac";
|
||||||
import { Sidebar } from "../layout/sidebar";
|
import { Sidebar } from "../layout/sidebar";
|
||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
@ -52,6 +52,9 @@ import { getDiForUnitTesting as getMainDi } from "../../../main/getDiForUnitTest
|
|||||||
import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels";
|
import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels";
|
||||||
import type { TrayMenuItem } from "../../../main/tray/tray-menu-item/tray-menu-item-injection-token";
|
import type { TrayMenuItem } from "../../../main/tray/tray-menu-item/tray-menu-item-injection-token";
|
||||||
import trayIconPathsInjectable from "../../../main/tray/tray-icon-path.injectable";
|
import trayIconPathsInjectable from "../../../main/tray/tray-icon-path.injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
import { openMenu } from "react-select-event";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
type Callback = (dis: DiContainers) => void | Promise<void>;
|
type Callback = (dis: DiContainers) => void | Promise<void>;
|
||||||
|
|
||||||
@ -85,6 +88,11 @@ export interface ApplicationBuilder {
|
|||||||
helmCharts: {
|
helmCharts: {
|
||||||
navigate: () => void;
|
navigate: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
select: {
|
||||||
|
openMenu: (id: string) => void;
|
||||||
|
selectOption: (menuId: string, labelText: string) => void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DiContainers {
|
interface DiContainers {
|
||||||
@ -433,6 +441,30 @@ export const getApplicationBuilder = () => {
|
|||||||
|
|
||||||
return rendered;
|
return rendered;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
select: {
|
||||||
|
openMenu: (menuId) => {
|
||||||
|
const selector = rendered.container.querySelector<HTMLElement>(
|
||||||
|
`#${menuId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(selector);
|
||||||
|
|
||||||
|
openMenu(selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectOption: (menuId, labelText) => {
|
||||||
|
const menuOptions = rendered.baseElement.querySelector<HTMLElement>(
|
||||||
|
`.${menuId}-options`,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(menuOptions);
|
||||||
|
|
||||||
|
const option = getByText(menuOptions, labelText);
|
||||||
|
|
||||||
|
userEvent.click(option);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
|||||||
@ -129,6 +129,8 @@ export interface WizardStepProps<D> extends WizardCommonProps<D> {
|
|||||||
skip?: boolean; // don't render the step
|
skip?: boolean; // don't render the step
|
||||||
scrollable?: boolean;
|
scrollable?: boolean;
|
||||||
children?: React.ReactNode | React.ReactNode[];
|
children?: React.ReactNode | React.ReactNode[];
|
||||||
|
testIdForNext?: string;
|
||||||
|
testIdForPrev?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WizardStepState {
|
interface WizardStepState {
|
||||||
@ -214,7 +216,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
|||||||
step, isFirst, isLast, children,
|
step, isFirst, isLast, children,
|
||||||
loading, customButtons, disabledNext, scrollable,
|
loading, customButtons, disabledNext, scrollable,
|
||||||
hideNextBtn, hideBackBtn, beforeContent, afterContent, noValidate, skip, moreButtons,
|
hideNextBtn, hideBackBtn, beforeContent, afterContent, noValidate, skip, moreButtons,
|
||||||
waiting, className, contentClass, prevLabel, nextLabel,
|
waiting, className, contentClass, prevLabel, nextLabel, testIdForNext, testIdForPrev,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
@ -242,6 +244,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
|||||||
label={prevLabel || (isFirst?.() ? "Cancel" : "Back")}
|
label={prevLabel || (isFirst?.() ? "Cancel" : "Back")}
|
||||||
hidden={hideBackBtn}
|
hidden={hideBackBtn}
|
||||||
onClick={this.prev}
|
onClick={this.prev}
|
||||||
|
data-testid={testIdForPrev}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
primary
|
primary
|
||||||
@ -250,6 +253,7 @@ export class WizardStep<D> extends React.Component<WizardStepProps<D>, WizardSte
|
|||||||
hidden={hideNextBtn}
|
hidden={hideNextBtn}
|
||||||
waiting={waiting ?? this.state.waiting}
|
waiting={waiting ?? this.state.waiting}
|
||||||
disabled={disabledNext}
|
disabled={disabledNext}
|
||||||
|
data-testid={testIdForNext}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -5,7 +5,12 @@
|
|||||||
|
|
||||||
import glob from "glob";
|
import glob from "glob";
|
||||||
import { memoize, noop } from "lodash/fp";
|
import { memoize, noop } from "lodash/fp";
|
||||||
import { createContainer } 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 { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
import requestFromChannelInjectable from "./utils/channel/request-from-channel.injectable";
|
import requestFromChannelInjectable from "./utils/channel/request-from-channel.injectable";
|
||||||
import loggerInjectable from "../common/logger.injectable";
|
import loggerInjectable from "../common/logger.injectable";
|
||||||
@ -45,6 +50,8 @@ import appVersionInjectable from "../common/get-configuration-file-model/app-ver
|
|||||||
import provideInitialValuesForSyncBoxesInjectable from "./utils/sync-box/provide-initial-values-for-sync-boxes.injectable";
|
import provideInitialValuesForSyncBoxesInjectable from "./utils/sync-box/provide-initial-values-for-sync-boxes.injectable";
|
||||||
import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable";
|
import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable";
|
||||||
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
|
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
|
||||||
|
import getFilePathsInjectable from "./components/+preferences/kubernetes/helm-charts/adding-of-custom-helm-repository/helm-file-input/get-file-paths.injectable";
|
||||||
|
import callForPublicHelmRepositoriesInjectable from "./components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
|
||||||
|
|
||||||
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
|
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
|
||||||
const {
|
const {
|
||||||
@ -91,9 +98,11 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
on: () => {},
|
on: () => {},
|
||||||
}) as unknown as IpcRenderer);
|
}) as unknown as IpcRenderer);
|
||||||
|
|
||||||
di.override(broadcastMessageInjectable, () => () => {
|
overrideFunctionalInjectables(di, [
|
||||||
throw new Error("Tried to broadcast message over IPC without explicit override.");
|
broadcastMessageInjectable,
|
||||||
});
|
getFilePathsInjectable,
|
||||||
|
callForPublicHelmRepositoriesInjectable,
|
||||||
|
]);
|
||||||
|
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||||
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
|
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
|
||||||
@ -147,3 +156,11 @@ const getInjectableFilePaths = memoize(() => [
|
|||||||
...glob.sync("../common/**/*.injectable.{ts,tsx}", { cwd: __dirname }),
|
...glob.sync("../common/**/*.injectable.{ts,tsx}", { cwd: __dirname }),
|
||||||
...glob.sync("../extensions/**/*.injectable.{ts,tsx}", { cwd: __dirname }),
|
...glob.sync("../extensions/**/*.injectable.{ts,tsx}", { cwd: __dirname }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
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.`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user