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:
parent
4b1d740d61
commit
b491a5c875
35
packages/core/src/common/cluster-env.injectable.ts
Normal file
35
packages/core/src/common/cluster-env.injectable.ts
Normal 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;
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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 = () => {
|
|
||||||
this.props.cluster.preferences.httpsProxy = this.proxy;
|
|
||||||
};
|
|
||||||
|
|
||||||
onChange = (value: string) => {
|
|
||||||
this.proxy = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SubTitle title="HTTP Proxy" id="http-proxy" />
|
<SubTitle title="HTTPS Proxy" id="http-proxy" />
|
||||||
<Input
|
<Input
|
||||||
theme="round-black"
|
theme="round-black"
|
||||||
value={this.proxy}
|
value={httpsProxy}
|
||||||
onChange={this.onChange}
|
onChange={setHttpsProxy}
|
||||||
onBlur={this.save}
|
onBlur={() => {
|
||||||
placeholder="http://<address>:<port>"
|
props.cluster.preferences.httpsProxy = httpsProxy;
|
||||||
validators={this.proxy ? InputValidators.isUrl : undefined}
|
}}
|
||||||
|
placeholder="https://<address>:<port>"
|
||||||
|
validators={httpsProxy ? InputValidators.isUrl : undefined}
|
||||||
/>
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
HTTP Proxy server. Used for communicating with Kubernetes API.
|
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't go through the default proxy.
|
||||||
</small>
|
</small>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user