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;
httpsProxy?: string;
noProxy?: string;
hiddenMetrics?: string[];
nodeShellImage?: 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 kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import clusterEnvironmentInjectable from "../../common/cluster-env.injectable";
export interface KubeAuthProxyServer {
getApiTarget(isLongRunningRequest?: boolean): Promise<ServerOptions>;
@ -26,6 +27,7 @@ const kubeAuthProxyServerInjectable = getInjectable({
instantiate: (di, cluster): KubeAuthProxyServer => {
const clusterUrl = new URL(cluster.apiUrl.get());
const clusterEnvironment = di.inject(clusterEnvironmentInjectable, cluster);
const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
const certificate = di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname);
@ -34,7 +36,10 @@ const kubeAuthProxyServerInjectable = getInjectable({
const ensureServerHelper = async (): Promise<KubeAuthProxy> => {
if (!kubeAuthProxy) {
const proxyEnv = Object.assign({}, process.env);
const proxyEnv = {
...process.env,
...clusterEnvironment.get(),
};
if (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 { KubeconfigManager } from "../kubeconfig-manager/kubeconfig-manager";
import type { AsyncResult } from "@k8slens/utilities";
import type { IComputedValue } from "mobx";
import type { ClusterEnvironment } from "../../common/cluster-env.injectable";
export interface ResourceApplierDependencies {
emitAppEvent: EmitAppEvent;
@ -27,6 +29,7 @@ export interface ResourceApplierDependencies {
createKubectl: CreateKubectl;
readonly proxyKubeconfigManager: KubeconfigManager;
readonly logger: Logger;
readonly clusterEnvironment: IComputedValue<ClusterEnvironment>;
}
export class ResourceApplier {
@ -100,17 +103,17 @@ export class ResourceApplier {
this.dependencies.logger.debug(`shooting manifests with ${kubectlPath}`, { args });
const execEnv = { ...process.env };
const httpsProxy = this.cluster.preferences?.httpsProxy;
if (httpsProxy) {
execEnv.HTTPS_PROXY = httpsProxy;
}
const execEnv = {
...process.env,
...this.dependencies.clusterEnvironment.get(),
};
try {
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) {
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 { Stat } from "../../common/fs/stat.injectable";
import type { IComputedValue } from "mobx";
import type { ClusterEnvironment } from "../../common/cluster-env.injectable";
export class ShellOpenError extends Error {
constructor(message: string, options?: ErrorOptions) {
@ -113,6 +114,7 @@ export interface ShellSessionDependencies {
readonly buildVersion: InitializableState<string>;
readonly proxyKubeconfigPath: string;
readonly directoryContainingKubectl: string;
readonly clusterEnvironment: IComputedValue<ClusterEnvironment>;
computeShellEnvironment: ComputeShellEnvironment;
spawnPty: SpawnPty;
emitAppEvent: EmitAppEvent;
@ -347,7 +349,10 @@ export abstract class ShellSession {
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);
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_VERSION = this.dependencies.buildVersion.get();
if (this.cluster.preferences.httpsProxy) {
env.HTTPS_PROXY = this.cluster.preferences.httpsProxy;
}
env.NO_PROXY = [
"localhost",
"127.0.0.1",

View File

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