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:
Sebastian Malton authored and GitHub committed 2022-11-24 08:46:53 -05:00
1 parent 055a13d4a9
commit 6142aad5ab
15 files changed
+343 -140

No files matched your search

@@ -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());
@@ -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;
@@ -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;
@@ -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;
@@ -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;
+16 -9
View File
@@ -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) {