From 9b9b8e0d056b56d432b26304e62f68b4def66418 Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:01:59 -0500 Subject: [PATCH] ShellEnv extension api (#4802) * adding extension api for terminal environment variables Signed-off-by: Jim Ehrismann * modified shell env api to work as a transformer (WIP) Signed-off-by: Jim Ehrismann * address some review comments Signed-off-by: Jim Ehrismann * shell env modifiers now take a CatalogEntity in ShellEnvContext param Signed-off-by: Jim Ehrismann * tweaks and bug fix Signed-off-by: Jim Ehrismann * refactored to remove ShellEnvModifier code from shell-session to local-shell-session Signed-off-by: Jim Ehrismann * further refactoring and documentation Signed-off-by: Jim Ehrismann * added comment Signed-off-by: Jim Ehrismann --- src/extensions/common-api/registrations.ts | 1 + src/extensions/lens-main-extension.ts | 17 ++++++++ .../local-shell-session.injectable.ts | 4 +- .../local-shell-session.ts | 14 ++++++- .../shell-env-modifier-registration.ts | 12 ++++++ .../terminal-shell-env-modifiers.ts | 39 +++++++++++++++++++ .../terminal-shell-env-modify.injectable.ts | 19 +++++++++ 7 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/main/shell-session/shell-env-modifier/shell-env-modifier-registration.ts create mode 100644 src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts create mode 100644 src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts diff --git a/src/extensions/common-api/registrations.ts b/src/extensions/common-api/registrations.ts index 794f203036..76d7538e12 100644 --- a/src/extensions/common-api/registrations.ts +++ b/src/extensions/common-api/registrations.ts @@ -11,3 +11,4 @@ export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../registries/page-menu-registry"; export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../registries/protocol-handler"; export type { CustomCategoryViewProps, CustomCategoryViewComponents, CustomCategoryViewRegistration } from "../../renderer/components/+catalog/custom-views"; +export type { ShellEnvModifier, ShellEnvContext } from "../../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts index a7c545ef11..bda6024522 100644 --- a/src/extensions/lens-main-extension.ts +++ b/src/extensions/lens-main-extension.ts @@ -10,10 +10,27 @@ import type { CatalogEntity } from "../common/catalog"; import type { IObservableArray } from "mobx"; import type { MenuRegistration } from "../main/menu/menu-registration"; import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration"; +import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; + export class LensMainExtension extends LensExtension { appMenus: MenuRegistration[] = []; trayMenus: TrayMenuRegistration[] = []; + /** + * implement this to modify the shell environment that Lens terminals are opened with. The ShellEnvModifier type has the signature + * + * (ctx: ShellEnvContext, env: Record) => Record + * + * @param ctx the shell environment context, specifically the relevant catalog entity for the terminal. This can be used, for example, to get + * cluster-specific information that can be made available in the shell environment by the implementation of terminalShellEnvModifier + * + * @param env the current shell environment that the terminal will be opened with. The implementation should modify this as desired. + * + * @returns the modified shell environment that the terminal will be opened with. The implementation must return env as passed in, if it + * does not modify the shell environment + */ + terminalShellEnvModifier?: ShellEnvModifier; + async navigate(pageId?: string, params?: Record, frameId?: number) { return WindowManager.getInstance().navigateExtension(this.id, pageId, params, frameId); } diff --git a/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts b/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts index 30ac1c2d2e..2578cc106d 100644 --- a/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts +++ b/src/main/shell-session/local-shell-session/local-shell-session.injectable.ts @@ -7,6 +7,7 @@ import { LocalShellSession } from "./local-shell-session"; import type { Cluster } from "../../../common/cluster/cluster"; import type WebSocket from "ws"; import createKubectlInjectable from "../../kubectl/create-kubectl.injectable"; +import terminalShellEnvModifiersInjectable from "../shell-env-modifier/terminal-shell-env-modify.injectable"; interface InstantiationParameter { webSocket: WebSocket; @@ -17,10 +18,11 @@ interface InstantiationParameter { const localShellSessionInjectable = getInjectable({ instantiate: (di, { cluster, tabId, webSocket }: InstantiationParameter) => { const createKubectl = di.inject(createKubectlInjectable); + const localShellEnvModify = di.inject(terminalShellEnvModifiersInjectable); const kubectl = createKubectl(cluster.version); - return new LocalShellSession(kubectl, webSocket, cluster, tabId); + return new LocalShellSession(localShellEnvModify, kubectl, webSocket, cluster, tabId); }, lifecycle: lifecycleEnum.transient, diff --git a/src/main/shell-session/local-shell-session/local-shell-session.ts b/src/main/shell-session/local-shell-session/local-shell-session.ts index ff7a3d34d4..301600bd71 100644 --- a/src/main/shell-session/local-shell-session/local-shell-session.ts +++ b/src/main/shell-session/local-shell-session/local-shell-session.ts @@ -3,14 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type WebSocket from "ws"; import path from "path"; import { helmCli } from "../../helm/helm-cli"; import { UserStore } from "../../../common/user-store"; +import type { Cluster } from "../../../common/cluster/cluster"; +import type { ClusterId } from "../../../common/cluster-types"; import { ShellSession } from "../shell-session"; +import type { Kubectl } from "../../kubectl/kubectl"; export class LocalShellSession extends ShellSession { ShellType = "shell"; + constructor(protected shellEnvModify: (clusterId: ClusterId, env: Record) => Record, kubectl: Kubectl, websocket: WebSocket, cluster: Cluster, terminalId: string) { + super(kubectl, websocket, cluster, terminalId); + } + protected getPathEntries(): string[] { return [helmCli.getBinaryDir()]; } @@ -20,7 +28,11 @@ export class LocalShellSession extends ShellSession { } public async open() { - const env = await this.getCachedShellEnv(); + let env = await this.getCachedShellEnv(); + + // extensions can modify the env + env = this.shellEnvModify(this.cluster.id, env); + const shell = env.PTYSHELL; const args = await this.getShellArgs(shell); diff --git a/src/main/shell-session/shell-env-modifier/shell-env-modifier-registration.ts b/src/main/shell-session/shell-env-modifier/shell-env-modifier-registration.ts new file mode 100644 index 0000000000..de08a51b3b --- /dev/null +++ b/src/main/shell-session/shell-env-modifier/shell-env-modifier-registration.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { CatalogEntity } from "../../../common/catalog"; + +export interface ShellEnvContext { + catalogEntity: CatalogEntity; +} + +export type ShellEnvModifier = (ctx: ShellEnvContext, env: Record) => Record; diff --git a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts new file mode 100644 index 0000000000..1486beaef9 --- /dev/null +++ b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modifiers.ts @@ -0,0 +1,39 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { computed, IComputedValue } from "mobx"; +import type { ClusterId } from "../../../common/cluster-types"; +import type { LensMainExtension } from "../../../extensions/lens-main-extension"; +import { catalogEntityRegistry } from "../../catalog"; + +interface Dependencies { + extensions: IComputedValue; +} + +export const terminalShellEnvModify = ({ extensions }: Dependencies) => + (clusterId: ClusterId, env: Record) => { + const terminalShellEnvModifiers = computed(() => ( + extensions.get() + .map((extension) => extension.terminalShellEnvModifier) + .filter(Boolean) + )) + .get(); + + if (terminalShellEnvModifiers.length === 0) { + return env; + } + + const entity = catalogEntityRegistry.getById(clusterId); + + if (entity) { + const ctx = { catalogEntity: entity }; + + // clone it so the passed value is not also modified + env = JSON.parse(JSON.stringify(env)); + env = terminalShellEnvModifiers.reduce((env, modifier) => modifier(ctx, env), env); + } + + return env; + }; diff --git a/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts new file mode 100644 index 0000000000..a6bd842f61 --- /dev/null +++ b/src/main/shell-session/shell-env-modifier/terminal-shell-env-modify.injectable.ts @@ -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, lifecycleEnum } from "@ogre-tools/injectable"; +import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; +import { terminalShellEnvModify } from "./terminal-shell-env-modifiers"; + +const terminalShellEnvModifyInjectable = getInjectable({ + instantiate: (di) => + terminalShellEnvModify({ + extensions: di.inject(mainExtensionsInjectable), + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default terminalShellEnvModifyInjectable;