mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make ResourceStack fully injectable (#6591)
Signed-off-by: Sebastian Malton <sebastian@malton.name> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
055a13d4a9
commit
6142aad5ab
@ -7,8 +7,6 @@ export const clusterActivateHandler = "cluster:activate";
|
||||
export const clusterSetFrameIdHandler = "cluster:set-frame-id";
|
||||
export const clusterVisibilityHandler = "cluster:visibility";
|
||||
export const clusterDisconnectHandler = "cluster:disconnect";
|
||||
export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all";
|
||||
export const clusterKubectlDeleteAllHandler = "cluster:kubectl-delete-all";
|
||||
export const clusterStates = "cluster:states";
|
||||
|
||||
/**
|
||||
|
||||
33
src/common/k8s/create-resource-stack.injectable.ts
Normal file
33
src/common/k8s/create-resource-stack.injectable.ts
Normal file
@ -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 type { KubernetesCluster } from "../catalog-entities";
|
||||
import readDirectoryInjectable from "../fs/read-directory.injectable";
|
||||
import readFileInjectable from "../fs/read-file.injectable";
|
||||
import { kubectlApplyAllInjectionToken, kubectlDeleteAllInjectionToken } from "../kube-helpers/channels";
|
||||
import loggerInjectable from "../logger.injectable";
|
||||
import joinPathsInjectable from "../path/join-paths.injectable";
|
||||
import type { ResourceApplyingStack, ResourceStackDependencies } from "./resource-stack";
|
||||
import { ResourceStack } from "./resource-stack";
|
||||
|
||||
export type CreateResourceStack = (cluster: KubernetesCluster, name: string) => ResourceApplyingStack;
|
||||
|
||||
const createResourceStackInjectable = getInjectable({
|
||||
id: "create-resource-stack",
|
||||
instantiate: (di): CreateResourceStack => {
|
||||
const deps: ResourceStackDependencies = {
|
||||
joinPaths: di.inject(joinPathsInjectable),
|
||||
kubectlApplyAll: di.inject(kubectlApplyAllInjectionToken),
|
||||
kubectlDeleteAll: di.inject(kubectlDeleteAllInjectionToken),
|
||||
logger: di.inject(loggerInjectable),
|
||||
readDirectory: di.inject(readDirectoryInjectable),
|
||||
readFile: di.inject(readFileInjectable),
|
||||
};
|
||||
|
||||
return (cluster, name) => new ResourceStack(deps, cluster, name);
|
||||
},
|
||||
});
|
||||
|
||||
export default createResourceStackInjectable;
|
||||
@ -2,22 +2,39 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import fse from "fs-extra";
|
||||
import path from "path";
|
||||
import hb from "handlebars";
|
||||
import type { KubernetesCluster } from "../catalog-entities";
|
||||
import logger from "../../main/logger";
|
||||
import { app } from "electron";
|
||||
import { ClusterStore } from "../cluster-store/cluster-store";
|
||||
import yaml from "js-yaml";
|
||||
import { requestKubectlApplyAll, requestKubectlDeleteAll } from "../../renderer/ipc";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import productNameInjectable from "../vars/product-name.injectable";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import createResourceApplierInjectable from "../../main/resource-applier/create-resource-applier.injectable";
|
||||
import type { AsyncResult } from "../utils/async-result";
|
||||
import type { Logger } from "../logger";
|
||||
import type { KubectlApplyAll, KubectlDeleteAll } from "../kube-helpers/channels";
|
||||
import type { ReadDirectory } from "../fs/read-directory.injectable";
|
||||
import type { JoinPaths } from "../path/join-paths.injectable";
|
||||
import type { ReadFile } from "../fs/read-file.injectable";
|
||||
import { hasTypedProperty, isObject } from "../utils";
|
||||
|
||||
export interface ResourceApplyingStack {
|
||||
kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string>;
|
||||
kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string>;
|
||||
}
|
||||
|
||||
export interface ResourceStackDependencies {
|
||||
readonly logger: Logger;
|
||||
kubectlApplyAll: KubectlApplyAll;
|
||||
kubectlDeleteAll: KubectlDeleteAll;
|
||||
readDirectory: ReadDirectory;
|
||||
joinPaths: JoinPaths;
|
||||
readFile: ReadFile;
|
||||
}
|
||||
|
||||
export class ResourceStack {
|
||||
constructor(protected cluster: KubernetesCluster, protected name: string) {}
|
||||
constructor(
|
||||
protected readonly dependencies: ResourceStackDependencies,
|
||||
protected readonly cluster: KubernetesCluster,
|
||||
protected readonly name: string,
|
||||
) {}
|
||||
|
||||
/**
|
||||
*
|
||||
@ -26,8 +43,15 @@ export class ResourceStack {
|
||||
*/
|
||||
async kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string> {
|
||||
const resources = await this.renderTemplates(folderPath, templateContext);
|
||||
const result = await this.applyResources(resources, extraArgs);
|
||||
|
||||
return this.applyResources(resources, extraArgs);
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
}
|
||||
|
||||
this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to apply resources: ${result.error}`);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,68 +61,43 @@ export class ResourceStack {
|
||||
*/
|
||||
async kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string> {
|
||||
const resources = await this.renderTemplates(folderPath, templateContext);
|
||||
const result = await this.deleteResources(resources, extraArgs);
|
||||
|
||||
return this.deleteResources(resources, extraArgs);
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
}
|
||||
|
||||
this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to delete resources: ${result.error}`);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
protected async applyResources(resources: string[], extraArgs?: string[]): Promise<string> {
|
||||
const clusterModel = ClusterStore.getInstance().getById(this.cluster.getId());
|
||||
protected async applyResources(resources: string[], extraArgs: string[] = []): Promise<AsyncResult<string, string>> {
|
||||
const kubectlArgs = [...extraArgs, ...this.getAdditionalArgs(extraArgs)];
|
||||
|
||||
if (!clusterModel) {
|
||||
throw new Error(`cluster not found`);
|
||||
}
|
||||
|
||||
let kubectlArgs = extraArgs || [];
|
||||
|
||||
kubectlArgs = this.appendKubectlArgs(kubectlArgs);
|
||||
|
||||
if (app) {
|
||||
const createResourceApplier = asLegacyGlobalFunctionForExtensionApi(createResourceApplierInjectable);
|
||||
|
||||
return await createResourceApplier(clusterModel).kubectlApplyAll(resources, kubectlArgs);
|
||||
} else {
|
||||
const response = await requestKubectlApplyAll(this.cluster.getId(), resources, kubectlArgs);
|
||||
|
||||
if (response.stderr) {
|
||||
throw new Error(response.stderr);
|
||||
}
|
||||
|
||||
return response.stdout ?? "";
|
||||
}
|
||||
return this.dependencies.kubectlApplyAll({
|
||||
clusterId: this.cluster.getId(),
|
||||
resources,
|
||||
extraArgs: kubectlArgs,
|
||||
});
|
||||
}
|
||||
|
||||
protected async deleteResources(resources: string[], extraArgs?: string[]): Promise<string> {
|
||||
const clusterModel = ClusterStore.getInstance().getById(this.cluster.getId());
|
||||
protected async deleteResources(resources: string[], extraArgs: string[] = []): Promise<AsyncResult<string, string>> {
|
||||
const kubectlArgs = [...extraArgs, ...this.getAdditionalArgs(extraArgs)];
|
||||
|
||||
if (!clusterModel) {
|
||||
throw new Error(`cluster not found`);
|
||||
}
|
||||
|
||||
let kubectlArgs = extraArgs || [];
|
||||
|
||||
kubectlArgs = this.appendKubectlArgs(kubectlArgs);
|
||||
|
||||
if (app) {
|
||||
const createResourceApplier = asLegacyGlobalFunctionForExtensionApi(createResourceApplierInjectable);
|
||||
|
||||
return await createResourceApplier(clusterModel).kubectlDeleteAll(resources, kubectlArgs);
|
||||
} else {
|
||||
const response = await requestKubectlDeleteAll(this.cluster.getId(), resources, kubectlArgs);
|
||||
|
||||
if (response.stderr) {
|
||||
throw new Error(response.stderr);
|
||||
}
|
||||
|
||||
return response.stdout ?? "";
|
||||
}
|
||||
return this.dependencies.kubectlDeleteAll({
|
||||
clusterId: this.cluster.getId(),
|
||||
resources,
|
||||
extraArgs: kubectlArgs,
|
||||
});
|
||||
}
|
||||
|
||||
protected appendKubectlArgs(kubectlArgs: string[]) {
|
||||
protected getAdditionalArgs(kubectlArgs: string[]): string[] {
|
||||
if (!kubectlArgs.includes("-l") && !kubectlArgs.includes("--label")) {
|
||||
return kubectlArgs.concat(["-l", `app.kubernetes.io/name=${this.name}`]);
|
||||
return ["-l", `app.kubernetes.io/name=${this.name}`];
|
||||
}
|
||||
|
||||
return kubectlArgs;
|
||||
return [];
|
||||
}
|
||||
|
||||
protected async renderTemplates(folderPath: string, templateContext: any): Promise<string[]> {
|
||||
@ -106,16 +105,16 @@ export class ResourceStack {
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
const productName = di.inject(productNameInjectable);
|
||||
|
||||
logger.info(`[RESOURCE-STACK]: render templates from ${folderPath}`);
|
||||
const files = await fse.readdir(folderPath);
|
||||
this.dependencies.logger.info(`[RESOURCE-STACK]: render templates from ${folderPath}`);
|
||||
const files = await this.dependencies.readDirectory(folderPath);
|
||||
|
||||
for(const filename of files) {
|
||||
const file = path.join(folderPath, filename);
|
||||
const raw = await fse.readFile(file);
|
||||
for (const filename of files) {
|
||||
const file = this.dependencies.joinPaths(folderPath, filename);
|
||||
const raw = await this.dependencies.readFile(file);
|
||||
const data = (
|
||||
filename.endsWith(".hb")
|
||||
? hb.compile(raw.toString())(templateContext)
|
||||
: raw.toString()
|
||||
? hb.compile(raw)(templateContext)
|
||||
: raw
|
||||
).trim();
|
||||
|
||||
if (!data) {
|
||||
@ -127,16 +126,15 @@ export class ResourceStack {
|
||||
continue;
|
||||
}
|
||||
|
||||
const resource = entry as Record<string, any>;
|
||||
if (hasTypedProperty(entry, "metadata", isObject)) {
|
||||
const labels = (entry.metadata.labels ??= {}) as Partial<Record<string, string>>;
|
||||
|
||||
if (typeof resource.metadata === "object") {
|
||||
resource.metadata.labels ??= {};
|
||||
resource.metadata.labels["app.kubernetes.io/name"] = this.name;
|
||||
resource.metadata.labels["app.kubernetes.io/managed-by"] = productName;
|
||||
resource.metadata.labels["app.kubernetes.io/created-by"] = "resource-stack";
|
||||
labels["app.kubernetes.io/name"] = this.name;
|
||||
labels["app.kubernetes.io/managed-by"] = productName;
|
||||
labels["app.kubernetes.io/created-by"] = "resource-stack";
|
||||
}
|
||||
|
||||
resources.push(yaml.dump(resource));
|
||||
resources.push(yaml.dump(entry));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
src/common/kube-helpers/channels.ts
Normal file
43
src/common/kube-helpers/channels.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 { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { Asyncify } from "type-fest";
|
||||
import type { RequestChannelHandler } from "../../main/utils/channel/channel-listeners/listener-tokens";
|
||||
import type { ClusterId } from "../cluster-types";
|
||||
import type { AsyncResult } from "../utils/async-result";
|
||||
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
|
||||
|
||||
export interface KubectlApplyAllArgs {
|
||||
clusterId: ClusterId;
|
||||
resources: string[];
|
||||
extraArgs: string[];
|
||||
}
|
||||
|
||||
export const kubectlApplyAllChannel: RequestChannel<KubectlApplyAllArgs, AsyncResult<string, string>> = {
|
||||
id: "kubectl-apply-all",
|
||||
};
|
||||
|
||||
export type KubectlApplyAll = Asyncify<RequestChannelHandler<typeof kubectlApplyAllChannel>>;
|
||||
|
||||
export const kubectlApplyAllInjectionToken = getInjectionToken<KubectlApplyAll>({
|
||||
id: "kubectl-apply-all",
|
||||
});
|
||||
|
||||
export interface KubectlDeleteAllArgs {
|
||||
clusterId: ClusterId;
|
||||
resources: string[];
|
||||
extraArgs: string[];
|
||||
}
|
||||
|
||||
export const kubectlDeleteAllChannel: RequestChannel<KubectlDeleteAllArgs, AsyncResult<string, string>> = {
|
||||
id: "kubectl-delete-all",
|
||||
};
|
||||
|
||||
export type KubectlDeleteAll = Asyncify<RequestChannelHandler<typeof kubectlDeleteAllChannel>>;
|
||||
|
||||
export const kubectlDeleteAllInjectionToken = getInjectionToken<KubectlDeleteAll>({
|
||||
id: "kubectl-delete-all",
|
||||
});
|
||||
@ -7,12 +7,14 @@
|
||||
// It is here to consolidate the common parts which are exported to `Main`
|
||||
// and to `Renderer`
|
||||
|
||||
export { ResourceStack } from "../../common/k8s/resource-stack";
|
||||
import apiManagerInjectable from "../../common/k8s-api/api-manager/manager.injectable";
|
||||
import createKubeApiForClusterInjectable from "../../common/k8s-api/create-kube-api-for-cluster.injectable";
|
||||
import createKubeApiForRemoteClusterInjectable from "../../common/k8s-api/create-kube-api-for-remote-cluster.injectable";
|
||||
import createResourceStackInjectable from "../../common/k8s/create-resource-stack.injectable";
|
||||
import type { ResourceApplyingStack } from "../../common/k8s/resource-stack";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||
import type { KubernetesCluster } from "./catalog";
|
||||
|
||||
export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable);
|
||||
export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable);
|
||||
@ -20,6 +22,27 @@ export const forRemoteCluster = asLegacyGlobalFunctionForExtensionApi(createKube
|
||||
|
||||
export { KubeApi } from "../../common/k8s-api/kube-api";
|
||||
|
||||
export const createResourceStack = asLegacyGlobalFunctionForExtensionApi(createResourceStackInjectable);
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using `Common.createResourceStack` instead
|
||||
*/
|
||||
export class ResourceStack implements ResourceApplyingStack {
|
||||
private readonly impl: ResourceApplyingStack;
|
||||
|
||||
constructor(cluster: KubernetesCluster, name: string) {
|
||||
this.impl = createResourceStack(cluster, name);
|
||||
}
|
||||
|
||||
kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[] | undefined): Promise<string> {
|
||||
return this.impl.kubectlApplyFolder(folderPath, templateContext, extraArgs);
|
||||
}
|
||||
|
||||
kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[] | undefined): Promise<string> {
|
||||
return this.impl.kubectlDeleteFolder(folderPath, templateContext, extraArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This type is unused
|
||||
*/
|
||||
|
||||
@ -12,7 +12,6 @@ import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.in
|
||||
import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
import applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
|
||||
import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable";
|
||||
import createResourceApplierInjectable from "../../../resource-applier/create-resource-applier.injectable";
|
||||
|
||||
const setupIpcMainHandlersInjectable = getInjectable({
|
||||
id: "setup-ipc-main-handlers",
|
||||
@ -25,7 +24,6 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
||||
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
|
||||
const askUserForFilePaths = di.inject(askUserForFilePathsInjectable);
|
||||
const emitAppEvent = di.inject(emitAppEventInjectable);
|
||||
const createResourceApplier = di.inject(createResourceApplierInjectable);
|
||||
|
||||
return {
|
||||
id: "setup-ipc-main-handlers",
|
||||
@ -39,7 +37,6 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
||||
operatingSystemTheme,
|
||||
askUserForFilePaths,
|
||||
emitAppEvent,
|
||||
createResourceApplier,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import type { IpcMainInvokeEvent } from "electron";
|
||||
import { BrowserWindow, Menu } from "electron";
|
||||
import { clusterFrameMap } from "../../../../common/cluster-frames";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler } from "../../../../common/ipc/cluster";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterDisconnectHandler } from "../../../../common/ipc/cluster";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
import { ClusterStore } from "../../../../common/cluster-store/cluster-store";
|
||||
import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../../common/ipc";
|
||||
@ -23,7 +23,6 @@ import type { Composite } from "../../../../common/utils/composite/get-composite
|
||||
import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable";
|
||||
import type { MenuItemRoot } from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
|
||||
import type { EmitAppEvent } from "../../../../common/app-event-bus/emit-event.injectable";
|
||||
import type { CreateResourceApplier } from "../../../resource-applier/create-resource-applier.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
applicationMenuItemComposite: IComputedValue<Composite<ApplicationMenuItemTypes | MenuItemRoot>>;
|
||||
@ -32,7 +31,6 @@ interface Dependencies {
|
||||
operatingSystemTheme: IComputedValue<Theme>;
|
||||
askUserForFilePaths: AskUserForFilePaths;
|
||||
emitAppEvent: EmitAppEvent;
|
||||
createResourceApplier: CreateResourceApplier;
|
||||
}
|
||||
|
||||
export const setupIpcMainHandlers = ({
|
||||
@ -42,7 +40,6 @@ export const setupIpcMainHandlers = ({
|
||||
operatingSystemTheme,
|
||||
askUserForFilePaths,
|
||||
emitAppEvent,
|
||||
createResourceApplier,
|
||||
}: Dependencies) => {
|
||||
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
return ClusterStore.getInstance()
|
||||
@ -71,44 +68,6 @@ export const setupIpcMainHandlers = ({
|
||||
}
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterKubectlApplyAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => {
|
||||
emitAppEvent({ name: "cluster", action: "kubectl-apply-all" });
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
const applier = createResourceApplier(cluster);
|
||||
|
||||
try {
|
||||
const stdout = await applier.kubectlApplyAll(resources, extraArgs);
|
||||
|
||||
return { stdout };
|
||||
} catch (error: any) {
|
||||
return { stderr: error };
|
||||
}
|
||||
} else {
|
||||
throw `${clusterId} is not a valid cluster id`;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterKubectlDeleteAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => {
|
||||
emitAppEvent({ name: "cluster", action: "kubectl-delete-all" });
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
const applier = createResourceApplier(cluster);
|
||||
|
||||
try {
|
||||
const stdout = await applier.kubectlDeleteAll(resources, extraArgs);
|
||||
|
||||
return { stdout };
|
||||
} catch (error: any) {
|
||||
return { stderr: error };
|
||||
}
|
||||
} else {
|
||||
throw `${clusterId} is not a valid cluster id`;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMainHandle(windowActionHandleChannel, (event, action) => handleWindowAction(action));
|
||||
|
||||
ipcMainOn(windowLocationChangedChannel, () => onLocationChange());
|
||||
|
||||
38
src/main/kubectl/apply-all-handler.injectable.ts
Normal file
38
src/main/kubectl/apply-all-handler.injectable.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable";
|
||||
import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable";
|
||||
import { kubectlApplyAllChannel } from "../../common/kube-helpers/channels";
|
||||
import createResourceApplierInjectable from "../resource-applier/create-resource-applier.injectable";
|
||||
import { getRequestChannelListenerInjectable } from "../utils/channel/channel-listeners/listener-tokens";
|
||||
|
||||
const kubectlApplyAllChannelHandlerInjectable = getRequestChannelListenerInjectable({
|
||||
channel: kubectlApplyAllChannel,
|
||||
handler: (di) => {
|
||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||
const emitAppEvent = di.inject(emitAppEventInjectable);
|
||||
const createResourceApplier = di.inject(createResourceApplierInjectable);
|
||||
|
||||
return async ({
|
||||
clusterId,
|
||||
extraArgs,
|
||||
resources,
|
||||
}) => {
|
||||
emitAppEvent({ name: "cluster", action: "kubectl-apply-all" });
|
||||
const cluster = getClusterById(clusterId);
|
||||
|
||||
if (!cluster) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `No cluster found for clusterId="${clusterId}"`,
|
||||
};
|
||||
}
|
||||
|
||||
return createResourceApplier(cluster).kubectlApplyAll(resources, extraArgs);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default kubectlApplyAllChannelHandlerInjectable;
|
||||
39
src/main/kubectl/delete-all-handler.injectable.ts
Normal file
39
src/main/kubectl/delete-all-handler.injectable.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable";
|
||||
import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable";
|
||||
import { kubectlDeleteAllChannel } from "../../common/kube-helpers/channels";
|
||||
import createResourceApplierInjectable from "../resource-applier/create-resource-applier.injectable";
|
||||
import { getRequestChannelListenerInjectable } from "../utils/channel/channel-listeners/listener-tokens";
|
||||
|
||||
const kubectlDeleteAllChannelHandlerInjectable = getRequestChannelListenerInjectable({
|
||||
channel: kubectlDeleteAllChannel,
|
||||
handler: (di) => {
|
||||
const emitAppEvent = di.inject(emitAppEventInjectable);
|
||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||
const createResourceApplier = di.inject(createResourceApplierInjectable);
|
||||
|
||||
return async ({
|
||||
clusterId,
|
||||
extraArgs,
|
||||
resources,
|
||||
}) => {
|
||||
emitAppEvent({ name: "cluster", action: "kubectl-delete-all" });
|
||||
|
||||
const cluster = getClusterById(clusterId);
|
||||
|
||||
if (!cluster) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `No cluster found for clusterId="${clusterId}"`,
|
||||
};
|
||||
}
|
||||
|
||||
return createResourceApplier(cluster).kubectlDeleteAll(resources, extraArgs);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default kubectlDeleteAllChannelHandlerInjectable;
|
||||
19
src/main/kubectl/kubectl-apply-all.injectable.ts
Normal file
19
src/main/kubectl/kubectl-apply-all.injectable.ts
Normal file
@ -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 { kubectlApplyAllInjectionToken } from "../../common/kube-helpers/channels";
|
||||
import kubectlApplyAllChannelHandlerInjectable from "./apply-all-handler.injectable";
|
||||
|
||||
const kubectlApplyAllInjectable = getInjectable({
|
||||
id: "kubectl-apply-all",
|
||||
instantiate: (di) => {
|
||||
const channelHandler = di.inject(kubectlApplyAllChannelHandlerInjectable);
|
||||
|
||||
return async (req) => channelHandler.handler(req);
|
||||
},
|
||||
injectionToken: kubectlApplyAllInjectionToken,
|
||||
});
|
||||
|
||||
export default kubectlApplyAllInjectable;
|
||||
19
src/main/kubectl/kubectl-delete-all.injectable.ts
Normal file
19
src/main/kubectl/kubectl-delete-all.injectable.ts
Normal file
@ -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 { kubectlDeleteAllInjectionToken } from "../../common/kube-helpers/channels";
|
||||
import kubectlDeleteAllChannelHandlerInjectable from "./delete-all-handler.injectable";
|
||||
|
||||
const kubectlDeleteAllInjectable = getInjectable({
|
||||
id: "kubectl-delete-all",
|
||||
instantiate: (di) => {
|
||||
const channel = di.inject(kubectlDeleteAllChannelHandlerInjectable);
|
||||
|
||||
return async (req) => channel.handler(req);
|
||||
},
|
||||
injectionToken: kubectlDeleteAllInjectionToken,
|
||||
});
|
||||
|
||||
export default kubectlDeleteAllInjectable;
|
||||
@ -14,6 +14,7 @@ import type { WriteFile } from "../../common/fs/write-file.injectable";
|
||||
import type { DeleteFile } from "../../common/fs/delete-file.injectable";
|
||||
import type { ExecFile } from "../../common/fs/exec-file.injectable";
|
||||
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
||||
import type { AsyncResult } from "../../common/utils/async-result";
|
||||
|
||||
export interface ResourceApplierDependencies {
|
||||
emitAppEvent: EmitAppEvent;
|
||||
@ -66,13 +67,13 @@ export class ResourceApplier {
|
||||
throw result.error.stderr || result.error.message;
|
||||
}
|
||||
|
||||
async create(resource: string): Promise<string> {
|
||||
async create(resource: string): Promise<AsyncResult<string, string>> {
|
||||
this.dependencies.emitAppEvent({ name: "resource", action: "apply" });
|
||||
|
||||
return this.kubectlApply(this.sanitizeObject(resource));
|
||||
}
|
||||
|
||||
protected async kubectlApply(content: string): Promise<string> {
|
||||
protected async kubectlApply(content: string): Promise<AsyncResult<string, string>> {
|
||||
const kubectl = await this.cluster.ensureKubectl();
|
||||
const kubectlPath = await kubectl.getPath();
|
||||
const proxyKubeconfigPath = await this.cluster.getProxyKubeconfigPath();
|
||||
@ -99,24 +100,27 @@ export class ResourceApplier {
|
||||
const result = await this.dependencies.execFile(kubectlPath, args);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
return result;
|
||||
}
|
||||
|
||||
throw result.error.stderr || result.error.message;
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: result.error.stderr || result.error.message,
|
||||
};
|
||||
} finally {
|
||||
await this.dependencies.deleteFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
public async kubectlApplyAll(resources: string[], extraArgs = ["-o", "json"]): Promise<string> {
|
||||
public async kubectlApplyAll(resources: string[], extraArgs = ["-o", "json"]): Promise<AsyncResult<string, string>> {
|
||||
return this.kubectlCmdAll("apply", resources, extraArgs);
|
||||
}
|
||||
|
||||
public async kubectlDeleteAll(resources: string[], extraArgs?: string[]): Promise<string> {
|
||||
public async kubectlDeleteAll(resources: string[], extraArgs?: string[]): Promise<AsyncResult<string, string>> {
|
||||
return this.kubectlCmdAll("delete", resources, extraArgs);
|
||||
}
|
||||
|
||||
protected async kubectlCmdAll(subCmd: string, resources: string[], parentArgs: string[] = []): Promise<string> {
|
||||
protected async kubectlCmdAll(subCmd: string, resources: string[], parentArgs: string[] = []): Promise<AsyncResult<string, string>> {
|
||||
const kubectl = await this.cluster.ensureKubectl();
|
||||
const kubectlPath = await kubectl.getPath();
|
||||
const proxyKubeconfigPath = await this.cluster.getProxyKubeconfigPath();
|
||||
@ -138,14 +142,17 @@ export class ResourceApplier {
|
||||
const result = await this.dependencies.execFile(kubectlPath, args);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
return result;
|
||||
}
|
||||
|
||||
this.dependencies.logger.error(`[RESOURCE-APPLIER] kubectl errored: ${result.error.message}`);
|
||||
|
||||
const splitError = result.error.stderr.split(`.yaml": `);
|
||||
|
||||
throw splitError[1] || result.error.message;
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: splitError[1] || result.error.message,
|
||||
};
|
||||
}
|
||||
|
||||
protected sanitizeObject(resource: string) {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import type { OpenDialogOptions } from "electron";
|
||||
import { clusterActivateHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/ipc/cluster";
|
||||
import { clusterActivateHandler, clusterDisconnectHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/ipc/cluster";
|
||||
import type { ClusterId, ClusterState } from "../../common/cluster-types";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel, type WindowAction } from "../../common/ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../common/ipc/dialog";
|
||||
@ -64,14 +64,6 @@ export function requestInitialClusterStates(): Promise<{ id: string; state: Clus
|
||||
return requestMain(clusterStates);
|
||||
}
|
||||
|
||||
export function requestKubectlApplyAll(clusterId: ClusterId, resources: string[], kubectlArgs: string[]): Promise<{ stderr?: string; stdout?: string }> {
|
||||
return requestMain(clusterKubectlApplyAllHandler, clusterId, resources, kubectlArgs);
|
||||
}
|
||||
|
||||
export function requestKubectlDeleteAll(clusterId: ClusterId, resources: string[], kubectlArgs: string[]): Promise<{ stderr?: string; stdout?: string }> {
|
||||
return requestMain(clusterKubectlDeleteAllHandler, clusterId, resources, kubectlArgs);
|
||||
}
|
||||
|
||||
export function requestInitialExtensionDiscovery(): Promise<{ isLoaded: boolean }> {
|
||||
return requestMain(extensionDiscoveryStateChannel);
|
||||
}
|
||||
|
||||
19
src/renderer/kubectl/apply-all.injectable.ts
Normal file
19
src/renderer/kubectl/apply-all.injectable.ts
Normal file
@ -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 { kubectlApplyAllChannel, kubectlApplyAllInjectionToken } from "../../common/kube-helpers/channels";
|
||||
import requestFromChannelInjectable from "../utils/channel/request-from-channel.injectable";
|
||||
|
||||
const kubectlApplyAllInjectable = getInjectable({
|
||||
id: "kubectl-apply-all",
|
||||
instantiate: (di) => {
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectable);
|
||||
|
||||
return (req) => requestFromChannel(kubectlApplyAllChannel, req);
|
||||
},
|
||||
injectionToken: kubectlApplyAllInjectionToken,
|
||||
});
|
||||
|
||||
export default kubectlApplyAllInjectable;
|
||||
19
src/renderer/kubectl/delete-all.injectable.ts
Normal file
19
src/renderer/kubectl/delete-all.injectable.ts
Normal file
@ -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 { kubectlDeleteAllChannel, kubectlDeleteAllInjectionToken } from "../../common/kube-helpers/channels";
|
||||
import requestFromChannelInjectable from "../utils/channel/request-from-channel.injectable";
|
||||
|
||||
const kubectlDeleteAllInjectable = getInjectable({
|
||||
id: "kubectl-delete-all",
|
||||
instantiate: (di) => {
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectable);
|
||||
|
||||
return (req) => requestFromChannel(kubectlDeleteAllChannel, req);
|
||||
},
|
||||
injectionToken: kubectlDeleteAllInjectionToken,
|
||||
});
|
||||
|
||||
export default kubectlDeleteAllInjectable;
|
||||
Loading…
Reference in New Issue
Block a user