mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Replace kubectl-proxy with lens-k8s-proxy (#4924)
This commit is contained in:
parent
f5a7d64551
commit
c04a3b8d33
98
build/download_k8s_proxy.ts
Normal file
98
build/download_k8s_proxy.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import packageInfo from "../package.json";
|
||||||
|
import fs from "fs";
|
||||||
|
import request from "request";
|
||||||
|
import { ensureDir, pathExists } from "fs-extra";
|
||||||
|
import path from "path";
|
||||||
|
import { noop } from "lodash";
|
||||||
|
import { isLinux, isMac } from "../src/common/vars";
|
||||||
|
|
||||||
|
class K8sProxyDownloader {
|
||||||
|
public version: string;
|
||||||
|
protected url: string;
|
||||||
|
protected path: string;
|
||||||
|
protected dirname: string;
|
||||||
|
|
||||||
|
constructor(version: string, platform: string, arch: string, target: string) {
|
||||||
|
this.version = version;
|
||||||
|
this.url = `https://github.com/lensapp/lens-k8s-proxy/releases/download/v${this.version}/lens-k8s-proxy-${platform}-${arch}`;
|
||||||
|
this.dirname = path.dirname(target);
|
||||||
|
this.path = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkBinary() {
|
||||||
|
const exists = await pathExists(this.path);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async download() {
|
||||||
|
if (await this.checkBinary()) {
|
||||||
|
return console.log("Already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureDir(path.dirname(this.path), 0o755);
|
||||||
|
|
||||||
|
const file = fs.createWriteStream(this.path);
|
||||||
|
|
||||||
|
console.log(`Downloading lens-k8s-proxy ${this.version} from ${this.url} to ${this.path}`);
|
||||||
|
const requestOpts: request.UriOptions & request.CoreOptions = {
|
||||||
|
uri: this.url,
|
||||||
|
gzip: true,
|
||||||
|
followAllRedirects: true,
|
||||||
|
};
|
||||||
|
const stream = request(requestOpts);
|
||||||
|
|
||||||
|
stream.on("complete", () => {
|
||||||
|
console.log("lens-k8s-proxy binary download finished");
|
||||||
|
file.end(noop);
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on("error", (error) => {
|
||||||
|
console.log(error);
|
||||||
|
fs.unlink(this.path, noop);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
file.on("close", () => {
|
||||||
|
console.log("lens-k8s-proxy binary download closed");
|
||||||
|
fs.chmod(this.path, 0o755, (err) => {
|
||||||
|
if (err) reject(err);
|
||||||
|
});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
stream.pipe(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const downloadVersion = packageInfo.config.k8sProxyVersion;
|
||||||
|
const baseDir = path.join(__dirname, "..", "binaries", "client");
|
||||||
|
|
||||||
|
const downloads = [];
|
||||||
|
|
||||||
|
if (isMac) {
|
||||||
|
downloads.push({ platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "lens-k8s-proxy") });
|
||||||
|
downloads.push({ platform: "darwin", arch: "arm64", target: path.join(baseDir, "darwin", "arm64", "lens-k8s-proxy") });
|
||||||
|
} else if (isLinux) {
|
||||||
|
downloads.push({ platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "lens-k8s-proxy") });
|
||||||
|
downloads.push({ platform: "linux", arch: "arm64", target: path.join(baseDir, "linux", "arm64", "lens-k8s-proxy") });
|
||||||
|
} else {
|
||||||
|
downloads.push({ platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "lens-k8s-proxy.exe") });
|
||||||
|
downloads.push({ platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "lens-k8s-proxy.exe") });
|
||||||
|
}
|
||||||
|
|
||||||
|
downloads.forEach((dlOpts) => {
|
||||||
|
console.log(dlOpts);
|
||||||
|
const downloader = new K8sProxyDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target);
|
||||||
|
|
||||||
|
console.log(`Downloading: ${JSON.stringify(dlOpts)}`);
|
||||||
|
downloader.download().then(() => downloader.checkBinary().then(() => console.log("Download complete")));
|
||||||
|
});
|
||||||
18
package.json
18
package.json
@ -34,6 +34,7 @@
|
|||||||
"download-bins": "concurrently yarn:download:*",
|
"download-bins": "concurrently yarn:download:*",
|
||||||
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
||||||
"download:helm": "yarn run ts-node build/download_helm.ts",
|
"download:helm": "yarn run ts-node build/download_helm.ts",
|
||||||
|
"download:k8s-proxy": "yarn run ts-node build/download_k8s_proxy.ts",
|
||||||
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
|
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
|
||||||
"build:theme-vars": "yarn run ts-node build/build_theme_vars.ts",
|
"build:theme-vars": "yarn run ts-node build/build_theme_vars.ts",
|
||||||
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
|
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
|
||||||
@ -47,6 +48,7 @@
|
|||||||
"postversion": "git push --set-upstream ${GIT_REMOTE:-origin} release/v$npm_package_version"
|
"postversion": "git push --set-upstream ${GIT_REMOTE:-origin} release/v$npm_package_version"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
"k8sProxyVersion": "0.1.2",
|
||||||
"bundledKubectlVersion": "1.23.3",
|
"bundledKubectlVersion": "1.23.3",
|
||||||
"bundledHelmVersion": "3.7.2",
|
"bundledHelmVersion": "3.7.2",
|
||||||
"sentryDsn": ""
|
"sentryDsn": ""
|
||||||
@ -129,6 +131,10 @@
|
|||||||
"from": "binaries/client/linux/${arch}/kubectl",
|
"from": "binaries/client/linux/${arch}/kubectl",
|
||||||
"to": "./${arch}/kubectl"
|
"to": "./${arch}/kubectl"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "binaries/client/linux/${arch}/lens-k8s-proxy",
|
||||||
|
"to": "./${arch}/lens-k8s-proxy"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/${arch}/helm3/helm3",
|
"from": "binaries/client/${arch}/helm3/helm3",
|
||||||
"to": "./helm3/helm3"
|
"to": "./helm3/helm3"
|
||||||
@ -150,6 +156,10 @@
|
|||||||
"from": "binaries/client/darwin/${arch}/kubectl",
|
"from": "binaries/client/darwin/${arch}/kubectl",
|
||||||
"to": "./${arch}/kubectl"
|
"to": "./${arch}/kubectl"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "binaries/client/darwin/${arch}/lens-k8s-proxy",
|
||||||
|
"to": "./${arch}/lens-k8s-proxy"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/${arch}/helm3/helm3",
|
"from": "binaries/client/${arch}/helm3/helm3",
|
||||||
"to": "./helm3/helm3"
|
"to": "./helm3/helm3"
|
||||||
@ -169,6 +179,14 @@
|
|||||||
"from": "binaries/client/windows/ia32/kubectl.exe",
|
"from": "binaries/client/windows/ia32/kubectl.exe",
|
||||||
"to": "./ia32/kubectl.exe"
|
"to": "./ia32/kubectl.exe"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "binaries/client/windows/x64/lens-k8s-proxy",
|
||||||
|
"to": "./x64/lens-k8s-proxy.exe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "binaries/client/windows/ia32/lens-k8s-proxy",
|
||||||
|
"to": "./ia32/lens-k8s-proxy.exe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"from": "binaries/client/x64/helm3/helm3.exe",
|
"from": "binaries/client/x64/helm3/helm3.exe",
|
||||||
"to": "./helm3/helm3.exe"
|
"to": "./helm3/helm3.exe"
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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 path from "path";
|
||||||
|
import isDevelopmentInjectable from "../../vars/is-development.injectable";
|
||||||
|
import contextDirInjectable from "../../vars/context-dir.injectable";
|
||||||
|
|
||||||
|
const directoryForBundledBinariesInjectable = getInjectable({
|
||||||
|
id: "directory-for-bundled-binaries",
|
||||||
|
instantiate: (di) => {
|
||||||
|
if (di.inject(isDevelopmentInjectable)) {
|
||||||
|
return path.join(di.inject(contextDirInjectable), "binaries");
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.resourcesPath;
|
||||||
|
},
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default directoryForBundledBinariesInjectable;
|
||||||
14
src/common/vars/context-dir.injectable.ts
Normal file
14
src/common/vars/context-dir.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 { contextDir } from "../vars";
|
||||||
|
|
||||||
|
const contextDirInjectable = getInjectable({
|
||||||
|
id: "context-dir",
|
||||||
|
instantiate: () => contextDir,
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default contextDirInjectable;
|
||||||
14
src/common/vars/is-development.injectable.ts
Normal file
14
src/common/vars/is-development.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 { isDevelopment } from "../vars";
|
||||||
|
|
||||||
|
const isDevelopmentInjectable = getInjectable({
|
||||||
|
id: "is-development",
|
||||||
|
instantiate: () => isDevelopment,
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default isDevelopmentInjectable;
|
||||||
@ -38,7 +38,7 @@ import type { Cluster } from "../../common/cluster/cluster";
|
|||||||
import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
|
import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
|
||||||
import { broadcastMessage } from "../../common/ipc";
|
import { broadcastMessage } from "../../common/ipc";
|
||||||
import { ChildProcess, spawn } from "child_process";
|
import { ChildProcess, spawn } from "child_process";
|
||||||
import { bundledKubectlPath, Kubectl } from "../kubectl/kubectl";
|
import { Kubectl } from "../kubectl/kubectl";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { waitUntilUsed } from "tcp-port-used";
|
import { waitUntilUsed } from "tcp-port-used";
|
||||||
import { EventEmitter, Readable } from "stream";
|
import { EventEmitter, Readable } from "stream";
|
||||||
@ -49,6 +49,7 @@ import mockFs from "mock-fs";
|
|||||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
console = new Console(stdout, stderr);
|
console = new Console(stdout, stderr);
|
||||||
|
|
||||||
@ -194,7 +195,7 @@ describe("kube auth proxy tests", () => {
|
|||||||
return mockedCP.stdout;
|
return mockedCP.stdout;
|
||||||
});
|
});
|
||||||
mockSpawn.mockImplementationOnce((command: string): ChildProcess => {
|
mockSpawn.mockImplementationOnce((command: string): ChildProcess => {
|
||||||
expect(command).toBe(bundledKubectlPath());
|
expect(path.basename(command).split(".")[0]).toBe("lens-k8s-proxy");
|
||||||
|
|
||||||
return mockedCP;
|
return mockedCP;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -118,7 +118,7 @@ export class ContextHandler {
|
|||||||
await this.ensureServer();
|
await this.ensureServer();
|
||||||
const path = this.clusterUrl.path !== "/" ? this.clusterUrl.path : "";
|
const path = this.clusterUrl.path !== "/" ? this.clusterUrl.path : "";
|
||||||
|
|
||||||
return `http://127.0.0.1:${this.kubeAuthProxy.port}${this.kubeAuthProxy.apiPrefix}${path}`;
|
return `http://127.0.0.1:${this.kubeAuthProxy.port}${this.kubeAuthProxy.apiPrefix.slice(0, -1)}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getApiTarget(isLongRunningRequest = false): Promise<httpProxy.ServerOptions> {
|
async getApiTarget(isLongRunningRequest = false): Promise<httpProxy.ServerOptions> {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import appNameInjectable from "./app-paths/app-name/app-name.injectable";
|
|||||||
import registerChannelInjectable from "./app-paths/register-channel/register-channel.injectable";
|
import registerChannelInjectable from "./app-paths/register-channel/register-channel.injectable";
|
||||||
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
|
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
|
||||||
import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
|
import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
|
||||||
|
import directoryForBundledBinariesInjectable from "../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
|
||||||
|
|
||||||
export const getDiForUnitTesting = (
|
export const getDiForUnitTesting = (
|
||||||
{ doGeneralOverrides } = { doGeneralOverrides: false },
|
{ doGeneralOverrides } = { doGeneralOverrides: false },
|
||||||
@ -43,6 +44,7 @@ export const getDiForUnitTesting = (
|
|||||||
di.override(setElectronAppPathInjectable, () => () => undefined);
|
di.override(setElectronAppPathInjectable, () => () => undefined);
|
||||||
di.override(appNameInjectable, () => "some-electron-app-name");
|
di.override(appNameInjectable, () => "some-electron-app-name");
|
||||||
di.override(registerChannelInjectable, () => () => undefined);
|
di.override(registerChannelInjectable, () => () => undefined);
|
||||||
|
di.override(directoryForBundledBinariesInjectable, () => "some-bin-directory");
|
||||||
|
|
||||||
di.override(writeJsonFileInjectable, () => () => {
|
di.override(writeJsonFileInjectable, () => () => {
|
||||||
throw new Error("Tried to write JSON file to file system without specifying explicit override.");
|
throw new Error("Tried to write JSON file to file system without specifying explicit override.");
|
||||||
|
|||||||
@ -5,16 +5,18 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { KubeAuthProxy } from "./kube-auth-proxy";
|
import { KubeAuthProxy } from "./kube-auth-proxy";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import bundledKubectlInjectable from "../kubectl/bundled-kubectl.injectable";
|
import path from "path";
|
||||||
|
import { isDevelopment, isWindows } from "../../common/vars";
|
||||||
|
import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
|
||||||
|
|
||||||
const createKubeAuthProxyInjectable = getInjectable({
|
const createKubeAuthProxyInjectable = getInjectable({
|
||||||
id: "create-kube-auth-proxy",
|
id: "create-kube-auth-proxy",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const bundledKubectl = di.inject(bundledKubectlInjectable);
|
const binaryName = isWindows ? "lens-k8s-proxy.exe" : "lens-k8s-proxy";
|
||||||
|
const proxyPath = isDevelopment ? path.join("client", process.platform, process.arch) : process.arch;
|
||||||
const dependencies = {
|
const dependencies = {
|
||||||
getProxyBinPath: bundledKubectl.getPath,
|
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), proxyPath, binaryName),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) =>
|
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) =>
|
||||||
|
|||||||
@ -8,18 +8,17 @@ import { waitUntilUsed } from "tcp-port-used";
|
|||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import * as url from "url";
|
|
||||||
import { getPortFrom } from "../utils/get-port";
|
import { getPortFrom } from "../utils/get-port";
|
||||||
import { makeObservable, observable, when } from "mobx";
|
import { makeObservable, observable, when } from "mobx";
|
||||||
|
|
||||||
const startingServeRegex = /^starting to serve on (?<address>.+)/i;
|
const startingServeRegex = /starting to serve on (?<address>.+)/i;
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
getProxyBinPath: () => Promise<string>;
|
proxyBinPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KubeAuthProxy {
|
export class KubeAuthProxy {
|
||||||
public readonly apiPrefix = `/${randomBytes(8).toString("hex")}`;
|
public readonly apiPrefix = `/${randomBytes(8).toString("hex")}/`;
|
||||||
|
|
||||||
public get port(): number {
|
public get port(): number {
|
||||||
return this._port;
|
return this._port;
|
||||||
@ -27,13 +26,10 @@ export class KubeAuthProxy {
|
|||||||
|
|
||||||
protected _port: number;
|
protected _port: number;
|
||||||
protected proxyProcess?: ChildProcess;
|
protected proxyProcess?: ChildProcess;
|
||||||
protected readonly acceptHosts: string;
|
|
||||||
@observable protected ready = false;
|
@observable protected ready = false;
|
||||||
|
|
||||||
constructor(private dependencies: Dependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) {
|
constructor(private dependencies: Dependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) {
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
|
||||||
this.acceptHosts = url.parse(this.cluster.apiUrl).hostname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get whenReady() {
|
get whenReady() {
|
||||||
@ -45,23 +41,16 @@ export class KubeAuthProxy {
|
|||||||
return this.whenReady;
|
return this.whenReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
const proxyBin = await this.dependencies.getProxyBinPath();
|
const proxyBin = this.dependencies.proxyBinPath;
|
||||||
const args = [
|
|
||||||
"proxy",
|
|
||||||
"-p", "0",
|
|
||||||
"--kubeconfig", `${this.cluster.kubeConfigPath}`,
|
|
||||||
"--context", `${this.cluster.contextName}`,
|
|
||||||
"--accept-hosts", this.acceptHosts,
|
|
||||||
"--reject-paths", "^[^/]",
|
|
||||||
"--api-prefix", this.apiPrefix,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (process.env.DEBUG_PROXY === "true") {
|
this.proxyProcess = spawn(proxyBin, [], {
|
||||||
args.push("-v", "9");
|
env: {
|
||||||
}
|
...this.env,
|
||||||
logger.debug(`spawning kubectl proxy with args: ${args}`);
|
KUBECONFIG: this.cluster.kubeConfigPath,
|
||||||
|
KUBECONFIG_CONTEXT: this.cluster.contextName,
|
||||||
this.proxyProcess = spawn(proxyBin, args, { env: this.env });
|
API_PREFIX: this.apiPrefix,
|
||||||
|
},
|
||||||
|
});
|
||||||
this.proxyProcess.on("error", (error) => {
|
this.proxyProcess.on("error", (error) => {
|
||||||
this.cluster.broadcastConnectUpdate(error.message, true);
|
this.cluster.broadcastConnectUpdate(error.message, true);
|
||||||
this.exit();
|
this.exit();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user