1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Add support for specifying NO_PROXY on a per cluster level

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-01-27 12:12:28 -05:00
parent 4b1d740d61
commit b491a5c875
6 changed files with 102 additions and 57 deletions

View File

@ -0,0 +1,35 @@
/**
* 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 { computed } from "mobx";
import type { Cluster } from "./cluster/cluster";
import { object } from "./utils";
function isDefinedEntry<T>(entry: readonly [T, string | undefined]): entry is [T, string] {
return Boolean(entry[1]);
}
export interface ClusterEnvironment {
HTTPS_PROXY?: string;
NO_PROXY?: string;
}
const clusterEnvironmentInjectable = getInjectable({
id: "cluster-environment",
instantiate: (di, cluster) => computed(() => {
const { preferences } = cluster;
const entries = [
["HTTPS_PROXY", preferences.httpsProxy],
["NO_PROXY", preferences.noProxy],
] as const;
return object.fromEntries(entries.filter(isDefinedEntry)) as ClusterEnvironment;
}),
lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (di, cluster: Cluster) => cluster.id,
}),
});
export default clusterEnvironmentInjectable;

View File

@ -106,6 +106,7 @@ export interface ClusterPreferences extends ClusterPrometheusPreferences {
*/ */
icon?: string | null; icon?: string | null;
httpsProxy?: string; httpsProxy?: string;
noProxy?: string;
hiddenMetrics?: string[]; hiddenMetrics?: string[];
nodeShellImage?: string; nodeShellImage?: string;
imagePullSecret?: string; imagePullSecret?: string;

View File

@ -9,6 +9,7 @@ import type { Cluster } from "../../common/cluster/cluster";
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable"; import kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import clusterEnvironmentInjectable from "../../common/cluster-env.injectable";
export interface KubeAuthProxyServer { export interface KubeAuthProxyServer {
getApiTarget(isLongRunningRequest?: boolean): Promise<ServerOptions>; getApiTarget(isLongRunningRequest?: boolean): Promise<ServerOptions>;
@ -26,6 +27,7 @@ const kubeAuthProxyServerInjectable = getInjectable({
instantiate: (di, cluster): KubeAuthProxyServer => { instantiate: (di, cluster): KubeAuthProxyServer => {
const clusterUrl = new URL(cluster.apiUrl.get()); const clusterUrl = new URL(cluster.apiUrl.get());
const clusterEnvironment = di.inject(clusterEnvironmentInjectable, cluster);
const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable); const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
const certificate = di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname); const certificate = di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname);
@ -34,7 +36,10 @@ const kubeAuthProxyServerInjectable = getInjectable({
const ensureServerHelper = async (): Promise<KubeAuthProxy> => { const ensureServerHelper = async (): Promise<KubeAuthProxy> => {
if (!kubeAuthProxy) { if (!kubeAuthProxy) {
const proxyEnv = Object.assign({}, process.env); const proxyEnv = {
...process.env,
...clusterEnvironment.get(),
};
if (cluster.preferences.httpsProxy) { if (cluster.preferences.httpsProxy) {
proxyEnv.HTTPS_PROXY = cluster.preferences.httpsProxy; proxyEnv.HTTPS_PROXY = cluster.preferences.httpsProxy;

View File

@ -17,6 +17,8 @@ import type { JoinPaths } from "../../common/path/join-paths.injectable";
import type { CreateKubectl } from "../kubectl/create-kubectl.injectable"; import type { CreateKubectl } from "../kubectl/create-kubectl.injectable";
import type { KubeconfigManager } from "../kubeconfig-manager/kubeconfig-manager"; import type { KubeconfigManager } from "../kubeconfig-manager/kubeconfig-manager";
import type { AsyncResult } from "@k8slens/utilities"; import type { AsyncResult } from "@k8slens/utilities";
import type { IComputedValue } from "mobx";
import type { ClusterEnvironment } from "../../common/cluster-env.injectable";
export interface ResourceApplierDependencies { export interface ResourceApplierDependencies {
emitAppEvent: EmitAppEvent; emitAppEvent: EmitAppEvent;
@ -27,6 +29,7 @@ export interface ResourceApplierDependencies {
createKubectl: CreateKubectl; createKubectl: CreateKubectl;
readonly proxyKubeconfigManager: KubeconfigManager; readonly proxyKubeconfigManager: KubeconfigManager;
readonly logger: Logger; readonly logger: Logger;
readonly clusterEnvironment: IComputedValue<ClusterEnvironment>;
} }
export class ResourceApplier { export class ResourceApplier {
@ -100,17 +103,17 @@ export class ResourceApplier {
this.dependencies.logger.debug(`shooting manifests with ${kubectlPath}`, { args }); this.dependencies.logger.debug(`shooting manifests with ${kubectlPath}`, { args });
const execEnv = { ...process.env }; const execEnv = {
const httpsProxy = this.cluster.preferences?.httpsProxy; ...process.env,
...this.dependencies.clusterEnvironment.get(),
if (httpsProxy) { };
execEnv.HTTPS_PROXY = httpsProxy;
}
try { try {
await this.dependencies.writeFile(fileName, content); await this.dependencies.writeFile(fileName, content);
const result = await this.dependencies.execFile(kubectlPath, args); const result = await this.dependencies.execFile(kubectlPath, args, {
env: execEnv,
});
if (result.callWasSuccessful) { if (result.callWasSuccessful) {
return result; return result;

View File

@ -19,6 +19,7 @@ import type { InitializableState } from "../../common/initializable-state/create
import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable"; import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable";
import type { Stat } from "../../common/fs/stat.injectable"; import type { Stat } from "../../common/fs/stat.injectable";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import type { ClusterEnvironment } from "../../common/cluster-env.injectable";
export class ShellOpenError extends Error { export class ShellOpenError extends Error {
constructor(message: string, options?: ErrorOptions) { constructor(message: string, options?: ErrorOptions) {
@ -113,6 +114,7 @@ export interface ShellSessionDependencies {
readonly buildVersion: InitializableState<string>; readonly buildVersion: InitializableState<string>;
readonly proxyKubeconfigPath: string; readonly proxyKubeconfigPath: string;
readonly directoryContainingKubectl: string; readonly directoryContainingKubectl: string;
readonly clusterEnvironment: IComputedValue<ClusterEnvironment>;
computeShellEnvironment: ComputeShellEnvironment; computeShellEnvironment: ComputeShellEnvironment;
spawnPty: SpawnPty; spawnPty: SpawnPty;
emitAppEvent: EmitAppEvent; emitAppEvent: EmitAppEvent;
@ -347,7 +349,10 @@ export abstract class ShellSession {
return process.env; return process.env;
})(); })();
const env = clearKubeconfigEnvVars(JSON.parse(JSON.stringify(rawEnv))); const env: Partial<Record<string, string>> = {
...clearKubeconfigEnvVars(JSON.parse(JSON.stringify(rawEnv))),
...this.dependencies.clusterEnvironment.get(),
};
const pathStr = [this.dependencies.directoryContainingKubectl, ...this.getPathEntries(), env.PATH].join(path.delimiter); const pathStr = [this.dependencies.directoryContainingKubectl, ...this.getPathEntries(), env.PATH].join(path.delimiter);
delete env.DEBUG; // don't pass DEBUG into shells delete env.DEBUG; // don't pass DEBUG into shells
@ -380,10 +385,6 @@ export abstract class ShellSession {
env.TERM_PROGRAM = this.dependencies.appName; env.TERM_PROGRAM = this.dependencies.appName;
env.TERM_PROGRAM_VERSION = this.dependencies.buildVersion.get(); env.TERM_PROGRAM_VERSION = this.dependencies.buildVersion.get();
if (this.cluster.preferences.httpsProxy) {
env.HTTPS_PROXY = this.cluster.preferences.httpsProxy;
}
env.NO_PROXY = [ env.NO_PROXY = [
"localhost", "localhost",
"127.0.0.1", "127.0.0.1",

View File

@ -3,9 +3,9 @@
* 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 React from "react"; import React, { useEffect, useState } from "react";
import { observable, autorun, makeObservable } from "mobx"; import { action } from "mobx";
import { observer, disposeOnUnmount } from "mobx-react"; import { observer } from "mobx-react";
import type { Cluster } from "../../../common/cluster/cluster"; import type { Cluster } from "../../../common/cluster/cluster";
import { Input, InputValidators } from "../input"; import { Input, InputValidators } from "../input";
import { SubTitle } from "../layout/sub-title"; import { SubTitle } from "../layout/sub-title";
@ -14,47 +14,47 @@ export interface ClusterProxySettingProps {
cluster: Cluster; cluster: Cluster;
} }
@observer export const ClusterProxySetting = observer((props: ClusterProxySettingProps) => {
export class ClusterProxySetting extends React.Component<ClusterProxySettingProps> { const { cluster } = props;
@observable proxy = "";
constructor(props: ClusterProxySettingProps) { const [httpsProxy, setHttpsProxy] = useState(cluster.preferences.httpsProxy || "");
super(props); const [noProxy, setNoProxy] = useState(cluster.preferences.noProxy || "");
makeObservable(this);
}
componentDidMount() { useEffect(() => action(() => {
disposeOnUnmount(this, cluster.preferences.httpsProxy = httpsProxy;
autorun(() => { cluster.preferences.noProxy = noProxy;
this.proxy = this.props.cluster.preferences.httpsProxy || ""; }), []);
}),
);
}
save = () => { return (
this.props.cluster.preferences.httpsProxy = this.proxy; <>
}; <SubTitle title="HTTPS Proxy" id="http-proxy" />
<Input
onChange = (value: string) => { theme="round-black"
this.proxy = value; value={httpsProxy}
}; onChange={setHttpsProxy}
onBlur={() => {
render() { props.cluster.preferences.httpsProxy = httpsProxy;
return ( }}
<> placeholder="https://<address>:<port>"
<SubTitle title="HTTP Proxy" id="http-proxy" /> validators={httpsProxy ? InputValidators.isUrl : undefined}
<Input />
theme="round-black" <small className="hint">
value={this.proxy} HTTPS Proxy server. Used for communicating with Kubernetes API.
onChange={this.onChange} </small>
onBlur={this.save} <SubTitle title="No Proxy" id="no-proxy" />
placeholder="http://<address>:<port>" <Input
validators={this.proxy ? InputValidators.isUrl : undefined} theme="round-black"
/> value={noProxy}
<small className="hint"> onChange={setNoProxy}
HTTP Proxy server. Used for communicating with Kubernetes API. onBlur={() => {
</small> props.cluster.preferences.noProxy = noProxy;
</> }}
); placeholder=""
} validators={noProxy ? InputValidators.isUrl : undefined}
} />
<small className="hint">
NO_PROXY configuration. Useful for when specifying that cluster communication shouldn&apos;t go through the default proxy.
</small>
</>
);
});