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

Merge branch 'master' into lens-k8s-proxy-tls

This commit is contained in:
Jari Kolehmainen 2022-03-10 15:06:14 +02:00 committed by GitHub
commit 9beb1d0da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
268 changed files with 9087 additions and 12770 deletions

View File

@ -146,6 +146,29 @@ module.exports = {
"named": "never",
"asyncArrow": "always",
}],
"@typescript-eslint/naming-convention": ["error",
{
"selector": "interface",
"format": ["PascalCase"],
"leadingUnderscore": "forbid",
"trailingUnderscore": "forbid",
"custom": {
"regex": "^Props$",
"match": false,
},
},
{
"selector": "typeAlias",
"format": ["PascalCase"],
"leadingUnderscore": "forbid",
"trailingUnderscore": "forbid",
"custom": {
"regex": "^(Props|State)$",
"match": false,
},
},
],
"@typescript-eslint/consistent-type-definitions": ["error", "interface"],
"unused-imports/no-unused-imports-ts": process.env.PROD === "true" ? "error" : "warn",
"unused-imports/no-unused-vars-ts": [
"warn", {

View File

@ -21,10 +21,7 @@ node_modules: yarn.lock
yarn check --verify-tree --integrity
binaries/client: node_modules
yarn download-bins
static/build/LensDev.html: node_modules
yarn compile:renderer
yarn download:binaries
.PHONY: compile-dev
compile-dev: node_modules
@ -32,7 +29,8 @@ compile-dev: node_modules
yarn compile:renderer --cache
.PHONY: dev
dev: binaries/client build-extensions static/build/LensDev.html
dev: binaries/client build-extensions
rm -rf static/build/
yarn dev
.PHONY: lint
@ -62,10 +60,11 @@ ifeq "$(DETECTED_OS)" "Windows"
endif
yarn run electron-builder --publish onTag $(ELECTRON_BUILDER_EXTRA_ARGS)
.NOTPARALLEL: $(extension_node_modules)
$(extension_node_modules): node_modules
cd $(@:/node_modules=) && ../../node_modules/.bin/npm install --no-audit --no-fund
cd $(@:/node_modules=) && ../../node_modules/.bin/npm install --no-audit --no-fund --no-save
$(extension_dists): src/extensions/npm/extensions/dist
$(extension_dists): src/extensions/npm/extensions/dist $(extension_node_modules)
cd $(@:/dist=) && ../../node_modules/.bin/npm run build
.PHONY: clean-old-extensions
@ -73,7 +72,7 @@ clean-old-extensions:
find ./extensions -mindepth 1 -maxdepth 1 -type d '!' -exec test -e '{}/package.json' \; -exec rm -rf {} \;
.PHONY: build-extensions
build-extensions: node_modules clean-old-extensions $(extension_node_modules) $(extension_dists)
build-extensions: node_modules clean-old-extensions $(extension_dists)
.PHONY: test-extensions
test-extensions: $(extension_node_modules)

235
build/download_binaries.ts Normal file
View File

@ -0,0 +1,235 @@
/**
* 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 { type WriteStream } from "fs";
import { FileHandle, open } from "fs/promises";
import { constants, ensureDir, unlink } from "fs-extra";
import path from "path";
import fetch from "node-fetch";
import { promisify } from "util";
import { pipeline as _pipeline, Transform, Writable } from "stream";
import { MultiBar, SingleBar } from "cli-progress";
import AbortController from "abort-controller";
import { extract } from "tar-stream";
import gunzip from "gunzip-maybe";
import { getBinaryName, normalizedPlatform } from "../src/common/vars";
const pipeline = promisify(_pipeline);
interface BinaryDownloaderArgs {
readonly version: string;
readonly platform: SupportedPlatform;
readonly downloadArch: string;
readonly fileArch: string;
readonly binaryName: string;
readonly baseDir: string;
}
abstract class BinaryDownloader {
protected abstract readonly url: string;
protected readonly bar: SingleBar;
protected readonly target: string;
protected getTransformStreams(file: Writable): (NodeJS.ReadWriteStream | NodeJS.WritableStream)[] {
return [file];
}
constructor(public readonly args: BinaryDownloaderArgs, multiBar: MultiBar) {
this.bar = multiBar.create(1, 0, args);
this.target = path.join(args.baseDir, args.platform, args.fileArch, args.binaryName);
}
async ensureBinary(): Promise<void> {
const controller = new AbortController();
const stream = await fetch(this.url, {
timeout: 15 * 60 * 1000, // 15min
signal: controller.signal,
});
const total = Number(stream.headers.get("content-length"));
const bar = this.bar;
let fileHandle: FileHandle;
if (isNaN(total)) {
throw new Error("no content-length header was present");
}
bar.setTotal(total);
await ensureDir(path.dirname(this.target), 0o755);
try {
/**
* This is necessary because for some reason `createWriteStream({ flags: "wx" })`
* was throwing someplace else and not here
*/
fileHandle = await open(this.target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL);
await pipeline(
stream.body,
new Transform({
transform(chunk, encoding, callback) {
bar.increment(chunk.length);
this.push(chunk);
callback();
},
}),
...this.getTransformStreams(new Writable({
write(chunk, encoding, cb) {
fileHandle.write(chunk)
.then(() => cb())
.catch(cb);
},
})),
);
await fileHandle.chmod(0o755);
await fileHandle.close();
} catch (error) {
await fileHandle?.close();
if (error.code === "EEXIST") {
bar.increment(total); // mark as finished
controller.abort(); // stop trying to download
} else {
await unlink(this.target);
throw error;
}
}
}
}
class LensK8sProxyDownloader extends BinaryDownloader {
protected readonly url: string;
constructor(args: Omit<BinaryDownloaderArgs, "binaryName">, bar: MultiBar) {
const binaryName = getBinaryName("lens-k8s-proxy", { forPlatform: args.platform });
super({ ...args, binaryName }, bar);
this.url = `https://github.com/lensapp/lens-k8s-proxy/releases/download/v${args.version}/lens-k8s-proxy-${args.platform}-${args.downloadArch}`;
}
}
class KubectlDownloader extends BinaryDownloader {
protected readonly url: string;
constructor(args: Omit<BinaryDownloaderArgs, "binaryName">, bar: MultiBar) {
const binaryName = getBinaryName("kubectl", { forPlatform: args.platform });
super({ ...args, binaryName }, bar);
this.url = `https://storage.googleapis.com/kubernetes-release/release/v${args.version}/bin/${args.platform}/${args.downloadArch}/${binaryName}`;
}
}
class HelmDownloader extends BinaryDownloader {
protected readonly url: string;
constructor(args: Omit<BinaryDownloaderArgs, "binaryName">, bar: MultiBar) {
const binaryName = getBinaryName("helm", { forPlatform: args.platform });
super({ ...args, binaryName }, bar);
this.url = `https://get.helm.sh/helm-v${args.version}-${args.platform}-${args.downloadArch}.tar.gz`;
}
protected getTransformStreams(file: WriteStream) {
const extracting = extract({
allowUnknownFormat: false,
});
extracting.on("entry", (headers, stream, next) => {
if (headers.name.endsWith(this.args.binaryName)) {
stream
.pipe(file)
.once("finish", () => next())
.once("error", next);
} else {
stream.resume();
next();
}
});
return [gunzip(3), extracting];
}
}
type SupportedPlatform = "darwin" | "linux" | "windows";
async function main() {
const multiBar = new MultiBar({
align: "left",
clearOnComplete: false,
hideCursor: true,
autopadding: true,
noTTYOutput: true,
format: "[{bar}] {percentage}% | {downloadArch} {binaryName}",
});
const baseDir = path.join(__dirname, "..", "binaries", "client");
const downloaders: BinaryDownloader[] = [
new LensK8sProxyDownloader({
version: packageInfo.config.k8sProxyVersion,
platform: normalizedPlatform,
downloadArch: "amd64",
fileArch: "x64",
baseDir,
}, multiBar),
new KubectlDownloader({
version: packageInfo.config.bundledKubectlVersion,
platform: normalizedPlatform,
downloadArch: "amd64",
fileArch: "x64",
baseDir,
}, multiBar),
new HelmDownloader({
version: packageInfo.config.bundledHelmVersion,
platform: normalizedPlatform,
downloadArch: "amd64",
fileArch: "x64",
baseDir,
}, multiBar),
];
if (normalizedPlatform === "darwin") {
downloaders.push(
new LensK8sProxyDownloader({
version: packageInfo.config.k8sProxyVersion,
platform: normalizedPlatform,
downloadArch: "arm64",
fileArch: "arm64",
baseDir,
}, multiBar),
new KubectlDownloader({
version: packageInfo.config.bundledKubectlVersion,
platform: normalizedPlatform,
downloadArch: "arm64",
fileArch: "arm64",
baseDir,
}, multiBar),
new HelmDownloader({
version: packageInfo.config.bundledHelmVersion,
platform: normalizedPlatform,
downloadArch: "arm64",
fileArch: "arm64",
baseDir,
}, multiBar),
);
}
const settledResults = await Promise.allSettled(downloaders.map(downloader => (
downloader.ensureBinary()
.catch(error => {
throw new Error(`Failed to download ${downloader.args.binaryName} for ${downloader.args.platform}/${downloader.args.downloadArch}: ${error}`);
})
)));
multiBar.stop();
const errorResult = settledResults.find(res => res.status === "rejected") as PromiseRejectedResult | undefined;
if (errorResult) {
console.error("234", String(errorResult.reason));
process.exit(1);
}
process.exit(0);
}
main().catch(error => console.error("from main", error));

View File

@ -1,19 +0,0 @@
/**
* 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 { isWindows } from "../src/common/vars";
import { HelmCli } from "../src/main/helm/helm-cli";
import * as path from "path";
const helmVersion = packageInfo.config.bundledHelmVersion;
if (!isWindows) {
Promise.all([
new HelmCli(path.join(process.cwd(), "binaries", "client", "x64"), helmVersion).ensureBinary(),
new HelmCli(path.join(process.cwd(), "binaries", "client", "arm64"), helmVersion).ensureBinary(),
]);
} else {
new HelmCli(path.join(process.cwd(), "binaries", "client", "x64"), helmVersion).ensureBinary();
}

View File

@ -1,98 +0,0 @@
/**
* 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")));
});

View File

@ -1,125 +0,0 @@
/**
* 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 md5File from "md5-file";
import requestPromise from "request-promise-native";
import { ensureDir, pathExists } from "fs-extra";
import path from "path";
import { noop } from "lodash";
import { isLinux, isMac } from "../src/common/vars";
class KubectlDownloader {
public kubectlVersion: string;
protected url: string;
protected path: string;
protected dirname: string;
constructor(clusterVersion: string, platform: string, arch: string, target: string) {
this.kubectlVersion = clusterVersion;
const binaryName = platform === "windows" ? "kubectl.exe" : "kubectl";
this.url = `https://storage.googleapis.com/kubernetes-release/release/v${this.kubectlVersion}/bin/${platform}/${arch}/${binaryName}`;
this.dirname = path.dirname(target);
this.path = target;
}
protected async urlEtag() {
const response = await requestPromise({
method: "HEAD",
uri: this.url,
resolveWithFullResponse: true,
}).catch(console.error);
if (response.headers["etag"]) {
return response.headers["etag"].replace(/"/g, "");
}
return "";
}
public async checkBinary() {
const exists = await pathExists(this.path);
if (exists) {
const hash = md5File.sync(this.path);
const etag = await this.urlEtag();
if (hash == etag) {
console.log("Kubectl md5sum matches the remote etag");
return true;
}
console.log(`Kubectl md5sum ${hash} does not match the remote etag ${etag}, unlinking and downloading again`);
await fs.promises.unlink(this.path);
}
return false;
}
public async downloadKubectl() {
if (await this.checkBinary()) {
return console.log("Already exists and is valid");
}
await ensureDir(path.dirname(this.path), 0o755);
const file = fs.createWriteStream(this.path);
console.log(`Downloading kubectl ${this.kubectlVersion} from ${this.url} to ${this.path}`);
const requestOpts: request.UriOptions & request.CoreOptions = {
uri: this.url,
gzip: true,
};
const stream = request(requestOpts);
stream.on("complete", () => {
console.log("kubectl 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("kubectl binary download closed");
fs.chmod(this.path, 0o755, (err) => {
if (err) reject(err);
});
resolve();
});
stream.pipe(file);
});
}
}
const downloadVersion = packageInfo.config.bundledKubectlVersion;
const baseDir = path.join(__dirname, "..", "binaries", "client");
const downloads = [];
if (isMac) {
downloads.push({ platform: "darwin", arch: "amd64", target: path.join(baseDir, "darwin", "x64", "kubectl") });
downloads.push({ platform: "darwin", arch: "arm64", target: path.join(baseDir, "darwin", "arm64", "kubectl") });
} else if (isLinux) {
downloads.push({ platform: "linux", arch: "amd64", target: path.join(baseDir, "linux", "x64", "kubectl") });
downloads.push({ platform: "linux", arch: "arm64", target: path.join(baseDir, "linux", "arm64", "kubectl") });
} else {
downloads.push({ platform: "windows", arch: "amd64", target: path.join(baseDir, "windows", "x64", "kubectl.exe") });
downloads.push({ platform: "windows", arch: "386", target: path.join(baseDir, "windows", "ia32", "kubectl.exe") });
}
downloads.forEach((dlOpts) => {
console.log(dlOpts);
const downloader = new KubectlDownloader(downloadVersion, dlOpts.platform, dlOpts.arch, dlOpts.target);
console.log(`Downloading: ${JSON.stringify(dlOpts)}`);
downloader.downloadKubectl().then(() => downloader.checkBinary().then(() => console.log("Download complete")));
});

View File

@ -724,11 +724,11 @@ const {
type Pod = Renderer.K8sApi.Pod;
interface Props {
interface PodsDetailsListProps {
pods?: Pod[];
}
export class PodsDetailsList extends React.Component<Props> {
export class PodsDetailsList extends React.Component<PodsDetailsListProps> {
getTableRow = (pod: Pod) => {
return (
<TableRow key={index} nowrap>

View File

@ -109,13 +109,13 @@ To allow the end-user to control the life cycle of this cluster feature the foll
}
} = Renderer;
interface Props {
interface ExampleClusterFeatureSettingsProps {
cluster: Common.Catalog.KubernetesCluster;
}
@observer
export class ExampleClusterFeatureSettings extends React.Component<Props> {
constructor(props: Props) {
export class ExampleClusterFeatureSettings extends React.Component<ExampleClusterFeatureSettingsProps> {
constructor(props: ExampleClusterFeatureSettingsProps) {
super(props);
makeObservable(this);
}

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,15 @@
"styles": []
},
"scripts": {
"build": "webpack && npm pack",
"dev": "webpack --watch",
"build": "npx webpack && npm pack",
"dev": "npx webpack -- --watch",
"test": "echo NO TESTS"
},
"files": [
"dist/**/*"
],
"dependencies": {},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"ts-loader": "latest",
"typescript": "latest",
"webpack": "latest"
"npm": "^8.5.3"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,9 @@
"styles": []
},
"scripts": {
"build": "webpack && npm pack",
"dev": "webpack --watch",
"test": "jest --passWithNoTests --env=jsdom src $@",
"build": "npx webpack && npm pack",
"dev": "npx webpack -- --watch",
"test": "npx jest --passWithNoTests --env=jsdom src $@",
"clean": "rm -rf dist/ && rm *.tgz"
},
"files": [
@ -19,10 +19,7 @@
],
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"semver": "^7.3.2",
"jest": "latest",
"ts-loader": "latest",
"typescript": "latest",
"webpack": "latest"
"npm": "^8.5.3",
"semver": "^7.3.2"
}
}

View File

@ -17,13 +17,13 @@ const {
},
} = Renderer;
interface Props {
export interface MetricsSettingsProps {
cluster: Common.Catalog.KubernetesCluster;
}
@observer
export class MetricsSettings extends React.Component<Props> {
constructor(props: Props) {
export class MetricsSettings extends React.Component<MetricsSettingsProps> {
constructor(props: MetricsSettingsProps) {
super(props);
makeObservable(this);
}

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,9 @@
"styles": []
},
"scripts": {
"build": "webpack && npm pack",
"dev": "webpack --watch",
"test": "jest --passWithNoTests --env=jsdom src $@"
"build": "npx webpack && npm pack",
"dev": "npx webpack -- --watch",
"test": "npx jest --passWithNoTests --env=jsdom src $@"
},
"files": [
"dist/**/*"
@ -18,9 +18,6 @@
"dependencies": {},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"jest": "latest",
"ts-loader": "latest",
"typescript": "latest",
"webpack": "latest"
"npm": "^8.5.3"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,9 @@
"styles": []
},
"scripts": {
"build": "webpack && npm pack",
"dev": "webpack --watch",
"test": "jest --passWithNoTests --env=jsdom src $@"
"build": "npx webpack && npm pack",
"dev": "npx webpack -- --watch",
"test": "npx jest --passWithNoTests --env=jsdom src $@"
},
"files": [
"dist/**/*"
@ -18,9 +18,6 @@
"dependencies": {},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"jest": "latest",
"ts-loader": "latest",
"typescript": "latest",
"webpack": "latest"
"npm": "^8.5.3"
}
}

View File

@ -3,7 +3,7 @@
"productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes",
"homepage": "https://github.com/lensapp/lens",
"version": "5.4.0-beta.5",
"version": "5.4.0",
"main": "static/build/main.js",
"copyright": "© 2021 OpenLens Authors",
"license": "MIT",
@ -15,10 +15,9 @@
"dev": "concurrently -i -k \"yarn run dev-run -C\" yarn:dev:*",
"dev-build": "concurrently yarn:compile:*",
"debug-build": "concurrently yarn:compile:main yarn:compile:extension-types",
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --remote-debugging-port=9223 --inspect .\"",
"dev-run": "nodemon --watch ./static/build/main.js --exec \"electron --remote-debugging-port=9223 --inspect .\"",
"dev:main": "yarn run compile:main --watch --progress",
"dev:renderer": "yarn run compile:renderer --watch --progress",
"dev:extension-types": "yarn run compile:extension-types --watch --progress",
"dev:renderer": "yarn run ts-node webpack.dev-server.ts",
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
"compile:main": "yarn run webpack --config webpack.main.ts",
"compile:renderer": "yarn run webpack --config webpack.renderer.ts",
@ -31,10 +30,7 @@
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
"dist": "yarn run compile && electron-builder --publish onTag",
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
"download-bins": "concurrently yarn:download:*",
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
"download:helm": "yarn run ts-node build/download_helm.ts",
"download:k8s-proxy": "yarn run ts-node build/download_k8s_proxy.ts",
"download:binaries": "yarn run ts-node build/download_binaries.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",
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
@ -48,7 +44,7 @@
"postversion": "git push --set-upstream ${GIT_REMOTE:-origin} release/v$npm_package_version"
},
"config": {
"k8sProxyVersion": "0.1.2",
"k8sProxyVersion": "0.1.5",
"bundledKubectlVersion": "1.23.3",
"bundledHelmVersion": "3.7.2",
"sentryDsn": ""
@ -136,8 +132,8 @@
"to": "./${arch}/lens-k8s-proxy"
},
{
"from": "binaries/client/${arch}/helm3/helm3",
"to": "./helm3/helm3"
"from": "binaries/client/linux/${arch}/helm",
"to": "./${arch}/helm"
}
]
},
@ -161,8 +157,8 @@
"to": "./${arch}/lens-k8s-proxy"
},
{
"from": "binaries/client/${arch}/helm3/helm3",
"to": "./helm3/helm3"
"from": "binaries/client/darwin/${arch}/helm",
"to": "./${arch}/helm"
}
]
},
@ -172,24 +168,16 @@
],
"extraResources": [
{
"from": "binaries/client/windows/x64/kubectl.exe",
"to": "./x64/kubectl.exe"
"from": "binaries/client/windows/${arch}/kubectl.exe",
"to": "./${arch}/kubectl.exe"
},
{
"from": "binaries/client/windows/ia32/kubectl.exe",
"to": "./ia32/kubectl.exe"
"from": "binaries/client/windows/${arch}/lens-k8s-proxy.exe",
"to": "./${arch}/lens-k8s-proxy.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",
"to": "./helm3/helm3.exe"
"from": "binaries/client/windows/${arch}/helm.exe",
"to": "./${arch}/helm.exe"
}
]
},
@ -274,7 +262,7 @@
"tar": "^6.1.11",
"tcp-port-used": "^1.0.2",
"tempy": "1.0.1",
"url-parse": "^1.5.3",
"url-parse": "^1.5.10",
"uuid": "^8.3.2",
"win-ca": "^3.4.5",
"winston": "^3.3.3",
@ -294,12 +282,14 @@
"@testing-library/user-event": "^13.5.0",
"@types/byline": "^4.2.33",
"@types/chart.js": "^2.9.34",
"@types/cli-progress": "^3.9.2",
"@types/color": "^3.0.2",
"@types/crypto-js": "^3.1.47",
"@types/dompurify": "^2.3.1",
"@types/electron-devtools-installer": "^2.2.0",
"@types/fs-extra": "^9.0.13",
"@types/glob-to-regexp": "^0.4.1",
"@types/gunzip-maybe": "^1.4.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/html-webpack-plugin": "^3.2.6",
"@types/http-proxy": "^1.17.7",
@ -332,6 +322,7 @@
"@types/sharp": "^0.29.4",
"@types/spdy": "^3.4.5",
"@types/tar": "^4.0.5",
"@types/tar-stream": "^2.2.2",
"@types/tcp-port-used": "^1.0.0",
"@types/tempy": "^0.3.0",
"@types/triple-beam": "^1.3.2",
@ -346,6 +337,7 @@
"ansi_up": "^5.1.0",
"chart.js": "^2.9.4",
"circular-dependency-plugin": "^5.2.2",
"cli-progress": "^3.10.0",
"color": "^3.2.1",
"concurrently": "^7.0.0",
"css-loader": "^6.5.1",
@ -364,6 +356,7 @@
"eslint-plugin-unused-imports": "^2.0.0",
"flex.box": "^3.4.4",
"fork-ts-checker-webpack-plugin": "^6.5.0",
"gunzip-maybe": "^1.4.2",
"hoist-non-react-statics": "^3.3.2",
"html-webpack-plugin": "^5.5.0",
"ignore-loader": "^0.1.2",
@ -394,6 +387,7 @@
"sharp": "^0.29.3",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.7",
"tar-stream": "^2.2.0",
"ts-jest": "26.5.6",
"ts-loader": "^9.2.6",
"ts-node": "^10.4.0",

View File

@ -43,7 +43,7 @@ users:
command: foo
`;
interface kubeconfig {
interface Kubeconfig {
apiVersion: string;
clusters: [{
name: string;
@ -66,7 +66,7 @@ interface kubeconfig {
preferences: {};
}
let mockKubeConfig: kubeconfig;
let mockKubeConfig: Kubeconfig;
describe("kube helpers", () => {
describe("validateKubeconfig", () => {

View File

@ -5,10 +5,10 @@
import { EventEmitter } from "../event-emitter";
export type AppEvent = {
export interface AppEvent {
name: string;
action: string;
params?: object;
};
}
export const appEventBus = new EventEmitter<[AppEvent]>();

View File

@ -3,19 +3,11 @@
* 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";
import { baseBinariesDir } from "../../vars";
const directoryForBundledBinariesInjectable = getInjectable({
id: "directory-for-bundled-binaries",
instantiate: (di) => {
if (di.inject(isDevelopmentInjectable)) {
return path.join(di.inject(contextDirInjectable), "binaries");
}
return process.resourcesPath;
},
instantiate: () => baseBinariesDir.get(),
lifecycle: lifecycleEnum.singleton,
});

View File

@ -14,9 +14,9 @@ export interface WebLinkStatus extends CatalogEntityStatus {
phase: WebLinkStatusPhase;
}
export type WebLinkSpec = {
export interface WebLinkSpec {
url: string;
};
}
export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus, WebLinkSpec> {
public static readonly apiVersion = "entity.k8slens.dev/v1alpha1";

View File

@ -0,0 +1,8 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export const setNativeThemeChannel = "theme:set-native-theme";
export const getNativeThemeChannel = "theme:get-native-theme";

View File

@ -36,13 +36,6 @@ describe("HelmChart tests", () => {
version: "!",
repo: "!",
} as any)).toThrowError('"created" is required');
expect(() => HelmChart.create({
apiVersion: "!",
name: "!",
version: "!",
repo: "!",
created: "!",
} as any)).toThrowError('"digest" is required');
});
it("should throw on fields being wrong type", () => {
@ -62,6 +55,14 @@ describe("HelmChart tests", () => {
created: "!",
digest: "!",
} as any)).toThrowError('"name" must be a string');
expect(() => HelmChart.create({
apiVersion: "!",
name: "!",
version: "!",
repo: "!",
created: "!",
digest: 1,
} as any)).toThrowError('"digest" must be a string');
expect(() => HelmChart.create({
apiVersion: "1",
name: "",

View File

@ -32,7 +32,7 @@ export class ApiManager {
return iter.find(this.apis.values(), api => api.kind === kind && api.apiVersionWithGroup === apiVersion);
}
registerApi(apiBase: string, api: KubeApi<KubeObject>) {
registerApi<K extends KubeObject>(apiBase: string, api: KubeApi<K>) {
if (!api.apiBase) return;
if (!this.apis.has(apiBase)) {
@ -46,13 +46,13 @@ export class ApiManager {
}
}
protected resolveApi<K extends KubeObject>(api?: string | KubeApi<K>): KubeApi<K> | undefined {
protected resolveApi(api?: string | KubeApi<KubeObject>): KubeApi<KubeObject> | undefined {
if (!api) {
return undefined;
}
if (typeof api === "string") {
return this.getApi(api) as KubeApi<K>;
return this.getApi(api) as KubeApi<KubeObject>;
}
return api;
@ -69,7 +69,7 @@ export class ApiManager {
}
@action
registerStore(store: KubeObjectStore<KubeObject>, apis: KubeApi<KubeObject>[] = [store.api]) {
registerStore<K extends KubeObject>(store: KubeObjectStore<K>, apis: KubeApi<K>[] = [store.api]) {
apis.filter(Boolean).forEach(api => {
if (api.apiBase) this.stores.set(api.apiBase, store);
});

View File

@ -9,12 +9,12 @@ import { crdResourcesURL } from "../../routes";
import { isClusterPageContext } from "../../utils/cluster-id-url-parsing";
import type { KubeJsonApiData } from "../kube-json-api";
type AdditionalPrinterColumnsCommon = {
interface AdditionalPrinterColumnsCommon {
name: string;
type: "integer" | "number" | "string" | "boolean" | "date";
priority: number;
description: string;
};
}
export type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
jsonPath: string;

View File

@ -75,7 +75,7 @@ export interface RawHelmChart {
version: string;
repo: string;
created: string;
digest: string;
digest?: string;
kubeVersion?: string;
description?: string;
home?: string;
@ -142,7 +142,7 @@ const helmChartValidator = Joi.object<HelmChart, true, RawHelmChart>({
.required(),
digest: Joi
.string()
.required(),
.optional(),
kubeVersion: Joi
.string()
.optional(),
@ -247,22 +247,22 @@ export interface HelmChart {
name: string;
version: string;
repo: string;
kubeVersion?: string;
created: string;
description: string;
digest: string;
keywords: string[];
home?: string;
sources: string[];
urls: string[];
annotations: Record<string, string>;
dependencies: HelmChartDependency[];
maintainers: HelmChartMaintainer[];
deprecated: boolean;
kubeVersion?: string;
digest?: string;
home?: string;
engine?: string;
icon?: string;
appVersion?: string;
type?: string;
deprecated: boolean;
tillerVersion?: string;
}
@ -324,7 +324,11 @@ export class HelmChart {
}
getId(): string {
return `${this.repo}:${this.apiVersion}/${this.name}@${this.getAppVersion()}+${this.digest}`;
const digestPart = this.digest
? `+${this.digest}`
: "";
return `${this.repo}:${this.apiVersion}/${this.name}@${this.getAppVersion()}${digestPart}`;
}
getName(): string {

View File

@ -216,7 +216,7 @@ export function ensureObjectSelfLink(api: KubeApi<KubeObject>, object: KubeJsonA
export type KubeApiWatchCallback = (data: IKubeWatchEvent<KubeJsonApiData>, error: any) => void;
export type KubeApiWatchOptions = {
export interface KubeApiWatchOptions {
namespace: string;
callback?: KubeApiWatchCallback;
abortController?: AbortController;
@ -225,7 +225,7 @@ export type KubeApiWatchOptions = {
// timeout in seconds
timeout?: number;
};
}
export type KubeApiPatchType = "merge" | "json" | "strategic";

View File

@ -55,7 +55,7 @@ describe("human format durations", () => {
});
test("durations less than 8 years returns years and days", () => {
const timeValue = Date.now() - new Date(moment().subtract(2, "years").subtract(5, "days").subtract(2, "hours").toDate()).getTime();
const timeValue = new Date(2020, 0, 10, 12, 0, 0, 0).getTime() - new Date(2018, 0, 4, 12, 0, 0, 0).getTime();
const res = formatDuration(timeValue);

View File

@ -40,9 +40,10 @@ export * from "./splitArray";
export * from "./tar";
export * from "./toJS";
export * from "./type-narrowing";
export * from "./types";
export * from "./wait-for-path";
export type { Tuple } from "./tuple";
import * as iter from "./iter";
import * as array from "./array";
import * as tuple from "./tuple";

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.
*/
/**
* A OnceCell is an object that wraps some function that produces a value.
*
* It then only calls the function on the first call to `get()` and returns the
* same instance/value on every subsequent call.
*/
export interface LazyInitialized<T> {
get(): T;
}
/**
* A function to make a `OnceCell<T>`
*/
export function lazyInitialized<T>(builder: () => T): LazyInitialized<T> {
let value: T | undefined;
let called = false;
return {
get() {
if (called) {
return value;
}
value = builder();
called = true;
return value;
},
};
}

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
type StaticThis<T, R extends any[]> = { new(...args: R): T };
interface StaticThis<T, R extends any[]> { new(...args: R): T }
export class Singleton {
private static instances = new WeakMap<object, Singleton>();

View File

@ -8,8 +8,14 @@ import * as array from "../utils/array";
/**
* A strict N-tuple of type T
*/
export type Tuple<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R["length"] extends N ? R : _TupleOf<T, N, [T, ...R]>;
export type Tuple<T, N extends number> = N extends N
? number extends N
? T[]
: TupleOfImpl<T, N, []>
: never;
type TupleOfImpl<T, N extends number, R extends unknown[]> = R["length"] extends N
? R
: TupleOfImpl<T, N, [T, ...R]>;
/**
* Iterates over `sources` yielding full tuples until one of the tuple arrays

View File

@ -1,10 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* An N length tuple of T
*/
export type Tuple<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R["length"] extends N ? R : _TupleOf<T, N, [T, ...R]>;

View File

@ -8,6 +8,7 @@ import path from "path";
import { SemVer } from "semver";
import packageInfo from "../../package.json";
import { defineGlobal } from "./utils/defineGlobal";
import { lazyInitialized } from "./utils/lazy-initialized";
export const isMac = process.platform === "darwin";
export const isWindows = process.platform === "win32";
@ -29,6 +30,54 @@ export const defaultTheme = "lens-dark" as string;
export const defaultFontSize = 12;
export const defaultTerminalFontFamily = "RobotoMono";
export const defaultEditorFontFamily = "RobotoMono";
export const normalizedPlatform = (() => {
switch (process.platform) {
case "darwin":
return "darwin";
case "linux":
return "linux";
case "win32":
return "windows";
default:
throw new Error(`platform=${process.platform} is unsupported`);
}
})();
export const normalizedArch = (() => {
switch (process.arch) {
case "arm64":
return "arm64";
case "x64":
case "amd64":
return "x64";
case "386":
case "x32":
case "ia32":
return "ia32";
default:
throw new Error(`arch=${process.arch} is unsupported`);
}
})();
export function getBinaryName(name: string, { forPlatform = normalizedPlatform } = {}): string {
if (forPlatform === "windows") {
return `${name}.exe`;
}
return name;
}
const resourcesDir = lazyInitialized(() => (
isProduction
? process.resourcesPath
: path.join(process.cwd(), "binaries", "client", normalizedPlatform)
));
export const baseBinariesDir = lazyInitialized(() => path.join(resourcesDir.get(), normalizedArch));
export const kubeAuthProxyBinaryName = getBinaryName("lens-k8s-proxy");
export const helmBinaryName = getBinaryName("helm");
export const helmBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), helmBinaryName));
export const kubectlBinaryName = getBinaryName("kubectl");
export const kubectlBinaryPath = lazyInitialized(() => path.join(baseBinariesDir.get(), kubectlBinaryName));
// Webpack build paths
export const contextDir = process.cwd();

View File

@ -6,7 +6,7 @@ export type { StatusBarRegistration } from "../../renderer/components/status-bar
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration";
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../renderer/components/+preferences/app-preferences/app-preference-registration";
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry";
export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry";
export type { KubeObjectStatusRegistration } from "../../renderer/components/kube-object-status-icon/kube-object-status-registration";
export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry";
export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../registries/page-menu-registry";
export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../registries/protocol-handler";

View File

@ -277,8 +277,6 @@ export class ExtensionLoader {
registries.ClusterPageRegistry.getInstance().add(extension.clusterPages, extension),
registries.ClusterPageMenuRegistry.getInstance().add(extension.clusterPageMenus, extension),
registries.KubeObjectDetailRegistry.getInstance().add(extension.kubeObjectDetailItems),
registries.KubeObjectStatusRegistry.getInstance().add(extension.kubeObjectStatusTexts),
registries.WorkloadsOverviewDetailRegistry.getInstance().add(extension.kubeWorkloadsOverviewItems),
];
this.events.on("remove", (removedExtension: LensRendererExtension) => {

View File

@ -20,18 +20,20 @@ import type { AdditionalCategoryColumnRegistration } from "../renderer/component
import type { CustomCategoryViewRegistration } from "../renderer/components/+catalog/custom-views";
import type { StatusBarRegistration } from "../renderer/components/status-bar/status-bar-registration";
import type { KubeObjectMenuRegistration } from "../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration";
import type { WorkloadsOverviewDetailRegistration } from "../renderer/components/+workloads-overview/workloads-overview-detail-registration";
import type { KubeObjectStatusRegistration } from "../renderer/components/kube-object-status-icon/kube-object-status-registration";
export class LensRendererExtension extends LensExtension {
globalPages: registries.PageRegistration[] = [];
clusterPages: registries.PageRegistration[] = [];
clusterPageMenus: registries.ClusterPageMenuRegistration[] = [];
kubeObjectStatusTexts: registries.KubeObjectStatusRegistration[] = [];
kubeObjectStatusTexts: KubeObjectStatusRegistration[] = [];
appPreferences: AppPreferenceRegistration[] = [];
entitySettings: registries.EntitySettingRegistration[] = [];
statusBarItems: StatusBarRegistration[] = [];
kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = [];
kubeObjectMenuItems: KubeObjectMenuRegistration[] = [];
kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = [];
kubeWorkloadsOverviewItems: WorkloadsOverviewDetailRegistration[] = [];
commands: CommandRegistration[] = [];
welcomeMenus: WelcomeMenuRegistration[] = [];
welcomeBanners: WelcomeBannerRegistration[] = [];

View File

@ -29,6 +29,10 @@ jest.mock("electron", () => ({
on: jest.fn(),
handle: jest.fn(),
},
ipcRenderer: {
on: jest.fn(),
invoke: jest.fn(),
},
}));
console = new Console(stdout, stderr);

View File

@ -8,8 +8,6 @@
export * from "./page-registry";
export * from "./page-menu-registry";
export * from "./kube-object-detail-registry";
export * from "./kube-object-status-registry";
export * from "./entity-setting-registry";
export * from "./catalog-entity-detail-registry";
export * from "./workloads-overview-detail-registry";
export * from "./protocol-handler";

View File

@ -1,29 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
import { BaseRegistry } from "./base-registry";
export interface KubeObjectStatusRegistration {
kind: string;
apiVersions: string[];
resolve: (object: KubeObject) => KubeObjectStatus;
}
export class KubeObjectStatusRegistry extends BaseRegistry<KubeObjectStatusRegistration> {
getItemsForKind(kind: string, apiVersion: string) {
return this.getItems()
.filter((item) => (
item.kind === kind
&& item.apiVersions.includes(apiVersion)
));
}
getItemsForObject(src: KubeObject) {
return this.getItemsForKind(src.kind, src.apiVersion)
.map(item => item.resolve(src))
.filter(Boolean);
}
}

View File

@ -1,31 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { orderBy } from "lodash";
import type React from "react";
import { BaseRegistry } from "./base-registry";
export interface WorkloadsOverviewDetailComponents {
Details: React.ComponentType<{}>;
}
export interface WorkloadsOverviewDetailRegistration {
components: WorkloadsOverviewDetailComponents;
priority?: number;
}
type RegisteredWorkloadsOverviewDetail = Required<WorkloadsOverviewDetailRegistration>;
export class WorkloadsOverviewDetailRegistry extends BaseRegistry<WorkloadsOverviewDetailRegistration, RegisteredWorkloadsOverviewDetail> {
getItems() {
return orderBy(super.getItems(), "priority", "desc");
}
protected getRegisteredItem(item: WorkloadsOverviewDetailRegistration): RegisteredWorkloadsOverviewDetail {
const { priority = 50, ...rest } = item;
return { priority, ...rest };
}
}

View File

@ -3,11 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export type KubeObjectStatus = {
export interface KubeObjectStatus {
level: KubeObjectStatusLevel;
text: string;
timestamp?: string;
};
}
export enum KubeObjectStatusLevel {
INFO = 1,

View File

@ -7,10 +7,10 @@ import type { RequestPromiseOptions } from "request-promise-native";
import type { Cluster } from "../../common/cluster/cluster";
import { k8sRequest } from "../k8s-request";
export type ClusterDetectionResult = {
export interface ClusterDetectionResult {
value: string | number | boolean;
accuracy: number;
};
}
export class BaseClusterDetector {
key: string;

View File

@ -121,7 +121,7 @@ export class ContextHandler {
await this.ensureServer();
const path = this.clusterUrl.path !== "/" ? this.clusterUrl.path : "";
return `https://127.0.0.1:${this.kubeAuthProxy.port}${this.kubeAuthProxy.apiPrefix.slice(0, -1)}${path}`;
return `http://127.0.0.1:${this.kubeAuthProxy.port}${this.kubeAuthProxy.apiPrefix}${path}`;
}
async getApiTarget(isLongRunningRequest = false): Promise<httpProxy.ServerOptions> {

23
src/main/helm/exec.ts Normal file
View File

@ -0,0 +1,23 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { promiseExecFile } from "../../common/utils/promise-exec";
import type { BaseEncodingOptions } from "fs";
import type { ExecFileOptions } from "child_process";
import { helmBinaryPath } from "../../common/vars";
/**
* ExecFile the bundled helm CLI
* @returns STDOUT
*/
export async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
try {
const { stdout } = await promiseExecFile(helmBinaryPath.get(), args, options);
return stdout;
} catch (error) {
throw error?.stderr || error;
}
}

View File

@ -8,10 +8,9 @@ import v8 from "v8";
import * as yaml from "js-yaml";
import type { HelmRepo } from "./helm-repo-manager";
import logger from "../logger";
import { promiseExecFile } from "../../common/utils/promise-exec";
import { helmCli } from "./helm-cli";
import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api";
import { iter, sortCharts } from "../../common/utils";
import { execHelm } from "./exec";
interface ChartCacheEntry {
data: Buffer;
@ -49,21 +48,13 @@ export class HelmChartManager {
}
private async executeCommand(args: string[], name: string, version?: string) {
const helm = await helmCli.binaryPath();
args.push(`${this.repo.name}/${name}`);
if (version) {
args.push("--version", version);
}
try {
const { stdout } = await promiseExecFile(helm, args);
return stdout;
} catch (error) {
throw error.stderr || error;
}
return execHelm(args);
}
public async getReadme(name: string, version?: string) {

View File

@ -1,49 +0,0 @@
/**
* 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 path from "path";
import { LensBinary, LensBinaryOpts } from "../lens-binary";
import { isProduction } from "../../common/vars";
export class HelmCli extends LensBinary {
public constructor(baseDir: string, version: string) {
const opts: LensBinaryOpts = {
version,
baseDir,
originalBinaryName: "helm",
newBinaryName: "helm3",
};
super(opts);
}
protected getTarName(): string | null {
return `${this.binaryName}-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`;
}
protected getUrl() {
return `https://get.helm.sh/helm-v${this.binaryVersion}-${this.platformName}-${this.arch}.tar.gz`;
}
protected getBinaryPath() {
return path.join(this.dirname, this.binaryName);
}
protected getOriginalBinaryPath() {
return path.join(this.dirname, `${this.platformName}-${this.arch}`, this.originalBinaryName);
}
}
const helmVersion = packageInfo.config.bundledHelmVersion;
let baseDir = process.resourcesPath;
if (!isProduction) {
baseDir = path.join(process.cwd(), "binaries", "client", process.arch);
}
export const helmCli = new HelmCli(baseDir, helmVersion);

View File

@ -6,23 +6,9 @@
import tempy from "tempy";
import fse from "fs-extra";
import * as yaml from "js-yaml";
import { promiseExecFile } from "../../common/utils/promise-exec";
import { helmCli } from "./helm-cli";
import { toCamelCase } from "../../common/utils/camelCase";
import type { BaseEncodingOptions } from "fs";
import { execFile, ExecFileOptions } from "child_process";
async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
const helmCliPath = await helmCli.binaryPath();
try {
const { stdout } = await promiseExecFile(helmCliPath, args, options);
return stdout;
} catch (error) {
throw error?.stderr || error;
}
}
import { execFile } from "child_process";
import { execHelm } from "./exec";
export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise<Record<string, any>[]> {
const args = [

View File

@ -4,14 +4,12 @@
*/
import yaml from "js-yaml";
import { BaseEncodingOptions, readFile } from "fs-extra";
import { promiseExecFile } from "../../common/utils/promise-exec";
import { helmCli } from "./helm-cli";
import { readFile } from "fs-extra";
import { Singleton } from "../../common/utils/singleton";
import { customRequestPromise } from "../../common/request";
import orderBy from "lodash/orderBy";
import logger from "../logger";
import type { ExecFileOptions } from "child_process";
import { execHelm } from "./exec";
export type HelmEnv = Record<string, string> & {
HELM_REPOSITORY_CACHE?: string;
@ -34,18 +32,6 @@ export interface HelmRepo {
password?: string;
}
async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise<string> {
const helmCliPath = await helmCli.binaryPath();
try {
const { stdout } = await promiseExecFile(helmCliPath, args, options);
return stdout;
} catch (error) {
throw error?.stderr || error;
}
}
export class HelmRepoManager extends Singleton {
protected repos: HelmRepo[];
protected helmEnv: HelmEnv;
@ -63,9 +49,6 @@ export class HelmRepoManager extends Singleton {
}
private async ensureInitialized() {
helmCli.setLogger(logger);
await helmCli.ensureBinary();
this.helmEnv ??= await this.parseHelmEnv();
const repos = await this.list();

View File

@ -7,10 +7,11 @@
import { injectSystemCAs } from "../common/system-ca";
import * as Mobx from "mobx";
import httpProxy from "http-proxy";
import * as LensExtensionsCommonApi from "../extensions/common-api";
import * as LensExtensionsMainApi from "../extensions/main-api";
import { app, autoUpdater, dialog, powerMonitor } from "electron";
import { appName, isIntegrationTesting, isMac, isWindows, productName, isDevelopment } from "../common/vars";
import { appName, isIntegrationTesting, isMac, isWindows, productName } from "../common/vars";
import { LensProxy } from "./lens-proxy";
import { WindowManager } from "./window-manager";
import { ClusterManager } from "./cluster-manager";
@ -55,6 +56,7 @@ import routerInjectable from "./router/router.injectable";
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
import userStoreInjectable from "../common/user-store/user-store.injectable";
import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable";
import { broadcastNativeThemeOnUpdate } from "./native-theme";
const di = getDi();
@ -108,6 +110,8 @@ di.runSetups().then(() => {
}
}
broadcastNativeThemeOnUpdate();
app.on("second-instance", (event, argv) => {
logger.debug("second-instance message");
@ -164,7 +168,7 @@ di.runSetups().then(() => {
const router = di.inject(routerInjectable);
const shellApiRequest = di.inject(shellApiRequestInjectable);
const lensProxy = LensProxy.createInstance(router, {
const lensProxy = LensProxy.createInstance(router, httpProxy.createProxy(), {
getClusterForRequest: (req) => ClusterManager.getInstance().getClusterForRequest(req),
kubeApiRequest,
shellApiRequest,
@ -227,16 +231,6 @@ di.runSetups().then(() => {
logger.info("🖥️ Starting WindowManager");
const windowManager = WindowManager.createInstance();
// Override main content view url to local webpack-dev-server to support HMR / live-reload
if (isDevelopment) {
const { createDevServer } = await import("../../webpack.dev-server");
const devServer = createDevServer(lensProxy.port);
await devServer.start();
windowManager.mainContentUrl = `http://localhost:${devServer.options.port}`;
}
const menuItems = di.inject(electronMenuItemsInjectable);
const trayMenuItems = di.inject(trayMenuItemsInjectable);

View File

@ -24,6 +24,8 @@ import { onLocationChange, handleWindowAction } from "../../ipc/window";
import { openFilePickingDialogChannel } from "../../../common/ipc/dialog";
import { showOpenDialog } from "../../ipc/dialog";
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../common/ipc/window";
import { getNativeColorTheme } from "../../native-theme";
import { getNativeThemeChannel } from "../../../common/ipc/native-theme";
interface Dependencies {
electronMenuItems: IComputedValue<MenuRegistration[]>;
@ -158,4 +160,8 @@ export const initIpcMainHandlers = ({ electronMenuItems, directoryForLensLocalSt
y: 20,
});
});
ipcMainHandle(getNativeThemeChannel, () => {
return getNativeColorTheme();
});
};

View File

@ -3,10 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { KubeAuthProxy } from "./kube-auth-proxy";
import { KubeAuthProxy, KubeAuthProxyDependencies } from "./kube-auth-proxy";
import type { Cluster } from "../../common/cluster/cluster";
import path from "path";
import { isDevelopment, isWindows } from "../../common/vars";
import { getBinaryName } from "../../common/vars";
import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
import getKubeAuthProxyCertDirInjectable from "./kube-auth-proxy-cert.injectable";
@ -14,15 +14,15 @@ const createKubeAuthProxyInjectable = getInjectable({
id: "create-kube-auth-proxy",
instantiate: (di) => {
const binaryName = isWindows ? "lens-k8s-proxy.exe" : "lens-k8s-proxy";
const proxyPath = isDevelopment ? path.join("client", process.platform, process.arch) : process.arch;
const dependencies = {
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), proxyPath, binaryName),
const binaryName = getBinaryName("lens-k8s-proxy");
const dependencies: KubeAuthProxyDependencies = {
proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName),
proxyCertPath: di.inject(getKubeAuthProxyCertDirInjectable),
};
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) =>
new KubeAuthProxy(dependencies, cluster, environmentVariables);
return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => (
new KubeAuthProxy(dependencies, cluster, environmentVariables)
);
},
});

View File

@ -13,13 +13,13 @@ import { makeObservable, observable, when } from "mobx";
const startingServeRegex = /starting to serve on (?<address>.+)/i;
interface Dependencies {
export interface KubeAuthProxyDependencies {
proxyBinPath: string;
proxyCertPath: string;
}
export class KubeAuthProxy {
public readonly apiPrefix = `/${randomBytes(8).toString("hex")}/`;
public readonly apiPrefix = `/${randomBytes(8).toString("hex")}`;
public get port(): number {
return this._port;
@ -29,7 +29,7 @@ export class KubeAuthProxy {
protected proxyProcess?: ChildProcess;
@observable protected ready = false;
constructor(private dependencies: Dependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) {
constructor(private dependencies: KubeAuthProxyDependencies, protected readonly cluster: Cluster, protected readonly env: NodeJS.ProcessEnv) {
makeObservable(this);
}

View File

@ -9,9 +9,8 @@ import { promiseExecFile } from "../../common/utils/promise-exec";
import logger from "../logger";
import { ensureDir, pathExists } from "fs-extra";
import * as lockFile from "proper-lockfile";
import { helmCli } from "../helm/helm-cli";
import { getBundledKubectlVersion } from "../../common/utils/app-version";
import { isDevelopment, isWindows, isTestEnv } from "../../common/vars";
import { normalizedPlatform, normalizedArch, kubectlBinaryName, kubectlBinaryPath, baseBinariesDir } from "../../common/vars";
import { SemVer } from "semver";
import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers";
import got from "got/dist/source";
@ -39,27 +38,8 @@ const kubectlMap: Map<string, string> = new Map([
["1.22", "1.22.6"],
["1.23", bundledVersion],
]);
let bundledPath: string;
const initScriptVersionString = "# lens-initscript v3";
export function bundledKubectlPath(): string {
if (bundledPath) { return bundledPath; }
if (isDevelopment || isTestEnv) {
const platformName = isWindows ? "windows" : process.platform;
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl");
} else {
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl");
}
if (isWindows) {
bundledPath = `${bundledPath}.exe`;
}
return bundledPath;
}
interface Dependencies {
directoryForKubectlBinaries: string;
@ -102,27 +82,13 @@ export class Kubectl {
logger.debug(`Set kubectl version ${this.kubectlVersion} for cluster version ${clusterVersion} using fallback`);
}
let arch = null;
if (process.arch == "x64") {
arch = "amd64";
} else if (process.arch == "x86" || process.arch == "ia32") {
arch = "386";
} else {
arch = process.arch;
}
const platformName = isWindows ? "windows" : process.platform;
const binaryName = isWindows ? "kubectl.exe" : "kubectl";
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`;
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${normalizedPlatform}/${normalizedArch}/${kubectlBinaryName}`;
this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion));
this.path = path.join(this.dirname, binaryName);
this.path = path.join(this.dirname, kubectlBinaryName);
}
public getBundledPath() {
return bundledKubectlPath();
return kubectlBinaryPath.get();
}
public getPathFromPreferences() {
@ -316,7 +282,7 @@ export class Kubectl {
? this.dirname
: path.dirname(this.getPathFromPreferences());
const helmPath = helmCli.getBinaryDir();
const binariesDir = baseBinariesDir.get();
const bashScriptPath = path.join(this.dirname, ".bash_set_path");
const bashScript = [
@ -330,7 +296,7 @@ export class Kubectl {
"elif test -f \"$HOME/.profile\"; then",
" . \"$HOME/.profile\"",
"fi",
`export PATH="${helmPath}:${kubectlPath}:$PATH"`,
`export PATH="${binariesDir}:${kubectlPath}:$PATH"`,
'export KUBECONFIG="$tempkubeconfig"',
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`,
@ -356,12 +322,12 @@ export class Kubectl {
// voodoo to replace any previous occurrences of kubectl path in the PATH
`kubectlpath="${kubectlPath}"`,
`helmpath="${helmPath}"`,
`binariesDir="${binariesDir}"`,
"p=\":$kubectlpath:\"",
"d=\":$PATH:\"",
`d=\${d//$p/:}`,
`d=\${d/#:/}`,
`export PATH="$helmpath:$kubectlpath:\${d/%:/}"`,
`export PATH="$binariesDir:$kubectlpath:\${d/%:/}"`,
"export KUBECONFIG=\"$tempkubeconfig\"",
`NO_PROXY=",\${NO_PROXY:-localhost},"`,
`NO_PROXY="\${NO_PROXY//,localhost,/,}"`,

View File

@ -1,202 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import path from "path";
import fs from "fs";
import request from "request";
import { ensureDir, pathExists } from "fs-extra";
import * as tar from "tar";
import { isWindows } from "../common/vars";
import type winston from "winston";
export type LensBinaryOpts = {
version: string;
baseDir: string;
originalBinaryName: string;
newBinaryName?: string;
requestOpts?: request.Options;
};
export class LensBinary {
public binaryVersion: string;
protected directory: string;
protected url: string;
protected path: string;
protected tarPath: string;
protected dirname: string;
protected binaryName: string;
protected platformName: string;
protected arch: string;
protected originalBinaryName: string;
protected requestOpts: request.Options;
protected logger: Console | winston.Logger;
constructor(opts: LensBinaryOpts) {
const baseDir = opts.baseDir;
this.originalBinaryName = opts.originalBinaryName;
this.binaryName = opts.newBinaryName || opts.originalBinaryName;
this.binaryVersion = opts.version;
this.requestOpts = opts.requestOpts;
this.logger = console;
let arch = null;
if (process.env.BINARY_ARCH) {
arch = process.env.BINARY_ARCH;
} else if (process.arch == "x64") {
arch = "amd64";
} else if (process.arch == "x86" || process.arch == "ia32") {
arch = "386";
} else {
arch = process.arch;
}
this.arch = arch;
this.platformName = isWindows ? "windows" : process.platform;
this.dirname = path.normalize(path.join(baseDir, this.binaryName));
if (isWindows) {
this.binaryName = `${this.binaryName}.exe`;
this.originalBinaryName = `${this.originalBinaryName}.exe`;
}
const tarName = this.getTarName();
if (tarName) {
this.tarPath = path.join(this.dirname, tarName);
}
}
public setLogger(logger: Console | winston.Logger) {
this.logger = logger;
}
protected binaryDir() {
throw new Error("binaryDir not implemented");
}
public async binaryPath() {
await this.ensureBinary();
return this.getBinaryPath();
}
protected getTarName(): string | null {
return null;
}
protected getUrl() {
return "";
}
protected getBinaryPath() {
return "";
}
protected getOriginalBinaryPath() {
return "";
}
public getBinaryDir() {
return path.dirname(this.getBinaryPath());
}
public async binDir() {
try {
await this.ensureBinary();
return this.dirname;
} catch (err) {
this.logger.error(err);
return "";
}
}
protected async checkBinary() {
const exists = await pathExists(this.getBinaryPath());
return exists;
}
public async ensureBinary() {
const isValid = await this.checkBinary();
if (!isValid) {
await this.downloadBinary().catch((error) => {
this.logger.error(error);
});
if (this.tarPath) await this.untarBinary();
if (this.originalBinaryName != this.binaryName) await this.renameBinary();
this.logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`);
}
}
protected async untarBinary() {
return new Promise<void>(resolve => {
this.logger.debug(`Extracting ${this.originalBinaryName} binary`);
tar.x({
file: this.tarPath,
cwd: this.dirname,
}).then((() => {
resolve();
}));
});
}
protected async renameBinary() {
return new Promise<void>((resolve, reject) => {
this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`);
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
}
protected async downloadBinary() {
const binaryPath = this.tarPath || this.getBinaryPath();
await ensureDir(this.getBinaryDir(), 0o755);
const file = fs.createWriteStream(binaryPath);
const url = this.getUrl();
this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`);
const requestOpts: request.UriOptions & request.CoreOptions = {
uri: url,
gzip: true,
...this.requestOpts,
};
const stream = request(requestOpts);
stream.on("complete", () => {
this.logger.info(`Download of ${this.originalBinaryName} finished`);
file.end();
});
stream.on("error", (error) => {
this.logger.error(error);
fs.unlink(binaryPath, () => {
// do nothing
});
throw(error);
});
return new Promise<void>((resolve, reject) => {
file.on("close", () => {
this.logger.debug(`${this.originalBinaryName} binary download closed`);
if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => {
if (err) reject(err);
});
resolve();
});
stream.pipe(file);
});
}
}

View File

@ -6,7 +6,7 @@
import type net from "net";
import type http from "http";
import spdy from "spdy";
import httpProxy from "http-proxy";
import type httpProxy from "http-proxy";
import { apiPrefix, apiKubePrefix } from "../common/vars";
import type { Router } from "./router";
import type { ContextHandler } from "./context-handler/context-handler";
@ -57,14 +57,15 @@ export class LensProxy extends Singleton {
protected proxyServer: http.Server;
protected closed = false;
protected retryCounters = new Map<string, number>();
protected proxy = this.createProxy();
protected getClusterForRequest: GetClusterForRequest;
public port: number;
constructor(protected router: Router, { shellApiRequest, kubeApiRequest, getClusterForRequest }: LensProxyFunctions) {
constructor(protected router: Router, protected proxy: httpProxy, { shellApiRequest, kubeApiRequest, getClusterForRequest }: LensProxyFunctions) {
super();
this.configureProxy(proxy);
this.getClusterForRequest = getClusterForRequest;
this.proxyServer = spdy.createServer({
@ -82,7 +83,9 @@ export class LensProxy extends Singleton {
const cluster = getClusterForRequest(req);
if (!cluster) {
return void logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
return socket.destroy();
}
const reqHandler = isInternal ? shellApiRequest : kubeApiRequest;
@ -163,9 +166,7 @@ export class LensProxy extends Singleton {
this.closed = true;
}
protected createProxy(): httpProxy {
const proxy = httpProxy.createProxyServer();
protected configureProxy(proxy: httpProxy): httpProxy {
proxy.on("proxyRes", (proxyRes, req, res) => {
const retryCounterId = this.getRequestId(req);

18
src/main/native-theme.ts Normal file
View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { nativeTheme } from "electron";
import { broadcastMessage } from "../common/ipc";
import { setNativeThemeChannel } from "../common/ipc/native-theme";
export function broadcastNativeThemeOnUpdate() {
nativeTheme.on("updated", () => {
broadcastMessage(setNativeThemeChannel, getNativeColorTheme());
});
}
export function getNativeColorTheme() {
return nativeTheme.shouldUseDarkColors ? "dark" : "light";
}

View File

@ -7,12 +7,12 @@ import type { CoreV1Api } from "@kubernetes/client-node";
import { inspect } from "util";
import { Singleton } from "../../common/utils";
export type PrometheusService = {
export interface PrometheusService {
id: string;
namespace: string;
service: string;
port: number;
};
}
export abstract class PrometheusProvider {
abstract readonly id: string;

View File

@ -6,6 +6,7 @@
import Call from "@hapi/call";
import Subtext from "@hapi/subtext";
import type http from "http";
import type httpProxy from "http-proxy";
import path from "path";
import { readFile } from "fs-extra";
import type { Cluster } from "../common/cluster/cluster";
@ -40,6 +41,7 @@ export interface LensApiRequest<P = any> {
query: URLSearchParams;
raw: {
req: http.IncomingMessage;
res: http.ServerResponse;
};
}
@ -62,6 +64,7 @@ function getMimeType(filename: string) {
interface Dependencies {
routePortForward: (request: LensApiRequest) => Promise<void>;
httpProxy?: httpProxy;
}
export class Router {
@ -101,7 +104,7 @@ export class Router {
cluster,
path: url.pathname,
raw: {
req,
req, res,
},
response: res,
query: url.searchParams,
@ -140,12 +143,26 @@ export class Router {
filePath = `${publicPath}/${appName}.html`;
}
}
}
protected addRoutes() {
// Static assets
this.router.add({ method: "get", path: "/{path*}" }, Router.handleStaticFile);
if (this.dependencies.httpProxy) {
this.router.add({ method: "get", path: "/{path*}" }, (apiReq: LensApiRequest) => {
const { req, res } = apiReq.raw;
if (req.url === "/" || !req.url.startsWith("/build/")) {
req.url = `${publicPath}/${appName}.html`;
}
this.dependencies.httpProxy.web(req, res, {
target: "http://127.0.0.1:8080",
});
});
} else {
this.router.add({ method: "get", path: "/{path*}" }, Router.handleStaticFile);
}
this.router.add({ method: "get", path: "/version" }, VersionRoute.getVersion);
this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, KubeconfigRoute.routeServiceAccountRoute);

View File

@ -3,6 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
import httpProxy from "http-proxy";
import { Router } from "../router";
import routePortForwardInjectable
from "../routes/port-forward/route-port-forward/route-port-forward.injectable";
@ -10,9 +12,15 @@ import routePortForwardInjectable
const routerInjectable = getInjectable({
id: "router",
instantiate: (di) => new Router({
routePortForward: di.inject(routePortForwardInjectable),
}),
instantiate: (di) => {
const isDevelopment = di.inject(isDevelopmentInjectable);
const proxy = isDevelopment ? httpProxy.createProxy() : undefined;
return new Router({
routePortForward: di.inject(routePortForwardInjectable),
httpProxy: proxy,
});
},
});
export default routerInjectable;

View File

@ -5,12 +5,12 @@
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";
import { baseBinariesDir } from "../../../common/vars";
export class LocalShellSession extends ShellSession {
ShellType = "shell";
@ -18,9 +18,9 @@ export class LocalShellSession extends ShellSession {
constructor(protected shellEnvModify: (clusterId: ClusterId, env: Record<string, string>) => Record<string, string>, kubectl: Kubectl, websocket: WebSocket, cluster: Cluster, terminalId: string) {
super(kubectl, websocket, cluster, terminalId);
}
protected getPathEntries(): string[] {
return [helmCli.getBinaryDir()];
return [baseBinariesDir.get()];
}
protected get cwd(): string | undefined {
@ -40,17 +40,16 @@ export class LocalShellSession extends ShellSession {
}
protected async getShellArgs(shell: string): Promise<string[]> {
const helmpath = helmCli.getBinaryDir();
const pathFromPreferences = UserStore.getInstance().kubectlBinariesPath || this.kubectl.getBundledPath();
const kubectlPathDir = UserStore.getInstance().downloadKubectlBinaries ? await this.kubectlBinDirP : path.dirname(pathFromPreferences);
switch(path.basename(shell)) {
case "powershell.exe":
return ["-NoExit", "-command", `& {$Env:PATH="${helmpath};${kubectlPathDir};$Env:PATH"}`];
return ["-NoExit", "-command", `& {$Env:PATH="${baseBinariesDir.get()};${kubectlPathDir};$Env:PATH"}`];
case "bash":
return ["--init-file", path.join(await this.kubectlBinDirP, ".bash_set_path")];
case "fish":
return ["--login", "--init-command", `export PATH="${helmpath}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`];
return ["--login", "--init-command", `export PATH="${baseBinariesDir.get()}:${kubectlPathDir}:$PATH"; export KUBECONFIG="${await this.kubeconfigPathP}"`];
case "zsh":
return ["--login"];
default:

View File

@ -76,9 +76,6 @@ export async function bootstrap(di: DiContainer) {
logger.info(`${logPrefix} initializing KubeObjectDetailRegistry`);
initializers.initKubeObjectDetailRegistry();
logger.info(`${logPrefix} initializing WorkloadsOverviewDetailRegistry`);
initializers.initWorkloadsOverviewDetailRegistry();
logger.info(`${logPrefix} initializing CatalogEntityDetailRegistry`);
initializers.initCatalogEntityDetailRegistry();

View File

@ -15,9 +15,9 @@ import { EventEmitter } from "events";
import { navigate } from "../../navigation";
import { catalogCategoryRegistry } from "../../api/catalog-category-registry";
export type CatalogAddButtonProps = {
export interface CatalogAddButtonProps {
category: CatalogCategory;
};
}
type CategoryId = string;

View File

@ -16,14 +16,14 @@ import { cssNames } from "../../utils";
import { Avatar } from "../avatar";
import { getLabelBadges } from "./helpers";
interface Props<T extends CatalogEntity> {
export interface CatalogEntityDetailsProps<T extends CatalogEntity> {
entity: T;
hideDetails(): void;
onRun: () => void;
}
@observer
export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Props<T>> {
export class CatalogEntityDetails<T extends CatalogEntity> extends Component<CatalogEntityDetailsProps<T>> {
categoryIcon(category: CatalogCategory) {
if (Icon.isSvg(category.metadata.icon)) {
return <Icon svg={category.metadata.icon} smallest />;

View File

@ -15,10 +15,10 @@ import { cssNames } from "../../utils";
import type { CatalogCategory } from "../../api/catalog-entity";
import { observer } from "mobx-react";
type Props = {
export interface CatalogMenuProps {
activeItem: string;
onItemClick: (id: string) => void;
};
}
function getCategories() {
return catalogCategoryRegistry.filteredItems;
@ -42,7 +42,7 @@ function Item(props: TreeItemProps) {
);
}
export const CatalogMenu = observer((props: Props) => {
export const CatalogMenu = observer((props: CatalogMenuProps) => {
return (
// Overwrite Material UI styles with injectFirst https://material-ui.com/guides/interoperability/#controlling-priority-4
<StylesProvider injectFirst>

View File

@ -41,6 +41,10 @@ jest.mock("electron", () => ({
on: jest.fn(),
handle: jest.fn(),
},
ipcRenderer: {
on: jest.fn(),
invoke: jest.fn(),
},
}));
jest.mock("./hotbar-toggle-menu-item", () => ({

View File

@ -37,7 +37,7 @@ import type { RegisteredCustomCategoryViewDecl } from "./custom-views.injectable
import customCategoryViewsInjectable from "./custom-views.injectable";
import type { CustomCategoryViewComponents } from "./custom-views";
interface Props extends RouteComponentProps<CatalogViewRouteParam> {}
export interface CatalogProps extends RouteComponentProps<CatalogViewRouteParam> {}
interface Dependencies {
catalogPreviousActiveTabStorage: { set: (value: string ) => void };
@ -47,11 +47,11 @@ interface Dependencies {
}
@observer
class NonInjectedCatalog extends React.Component<Props & Dependencies> {
class NonInjectedCatalog extends React.Component<CatalogProps & Dependencies> {
@observable private contextMenu: CatalogEntityContextMenuContext;
@observable activeTab?: string;
constructor(props: Props & Dependencies) {
constructor(props: CatalogProps & Dependencies) {
super(props);
makeObservable(this);
}
@ -305,7 +305,7 @@ class NonInjectedCatalog extends React.Component<Props & Dependencies> {
}
}
export const Catalog = withInjectables<Dependencies, Props>( NonInjectedCatalog, {
export const Catalog = withInjectables<Dependencies, CatalogProps>( NonInjectedCatalog, {
getProps: (di, props) => ({
catalogEntityStore: di.inject(catalogEntityStoreInjectable),
catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable),

View File

@ -20,7 +20,7 @@ import { ThemeStore } from "../../theme.store";
import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params";
import { apiManager } from "../../../common/k8s-api/api-manager";
interface Props {
export interface ClusterIssuesProps {
className?: string;
}
@ -39,14 +39,14 @@ enum sortBy {
}
@observer
export class ClusterIssues extends React.Component<Props> {
export class ClusterIssues extends React.Component<ClusterIssuesProps> {
private sortCallbacks = {
[sortBy.type]: (warning: IWarning) => warning.kind,
[sortBy.object]: (warning: IWarning) => warning.getName(),
[sortBy.age]: (warning: IWarning) => warning.timeDiffFromNow,
};
constructor(props: Props) {
constructor(props: ClusterIssuesProps) {
super(props);
makeObservable(this);
}

View File

@ -7,11 +7,11 @@ import React from "react";
import { Icon } from "../icon";
import { cssNames } from "../../utils";
interface Props {
export interface ClusterNoMetricsProps {
className: string;
}
export function ClusterNoMetrics({ className }: Props) {
export function ClusterNoMetrics({ className }: ClusterNoMetricsProps) {
return (
<div className={cssNames("ClusterNoMetrics flex column box grow justify-center align-center", className)}>
<Icon material="info"/>

View File

@ -27,11 +27,11 @@ enum columnId {
status = "status",
}
interface Props extends RouteComponentProps<HpaRouteParams> {
export interface HorizontalPodAutoscalersProps extends RouteComponentProps<HpaRouteParams> {
}
@observer
export class HorizontalPodAutoscalers extends React.Component<Props> {
export class HorizontalPodAutoscalers extends React.Component<HorizontalPodAutoscalersProps> {
getTargets(hpa: HorizontalPodAutoscaler) {
const metrics = hpa.getMetrics();

View File

@ -14,7 +14,7 @@ import { DrawerItem } from "../drawer/drawer-item";
import { Badge } from "../badge";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<LimitRange> {
export interface LimitRangeDetailsProps extends KubeObjectDetailsProps<LimitRange> {
}
function renderLimit(limit: LimitRangeItem, part: LimitPart, resource: Resource) {
@ -52,7 +52,7 @@ function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) {
}
@observer
export class LimitRangeDetails extends React.Component<Props> {
export class LimitRangeDetails extends React.Component<LimitRangeDetailsProps> {
render() {
const { object: limitRange } = this.props;

View File

@ -19,11 +19,11 @@ enum columnId {
age = "age",
}
interface Props extends RouteComponentProps<LimitRangeRouteParams> {
export interface LimitRangesProps extends RouteComponentProps<LimitRangeRouteParams> {
}
@observer
export class LimitRanges extends React.Component<Props> {
export class LimitRanges extends React.Component<LimitRangesProps> {
render() {
return (
<KubeObjectListLayout

View File

@ -18,15 +18,15 @@ import { ConfigMap } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<ConfigMap> {
export interface ConfigMapDetailsProps extends KubeObjectDetailsProps<ConfigMap> {
}
@observer
export class ConfigMapDetails extends React.Component<Props> {
export class ConfigMapDetails extends React.Component<ConfigMapDetailsProps> {
@observable isSaving = false;
@observable data = observable.map<string, string>();
constructor(props: Props) {
constructor(props: ConfigMapDetailsProps) {
super(props);
makeObservable(this);
}

View File

@ -20,11 +20,11 @@ enum columnId {
age = "age",
}
interface Props extends RouteComponentProps<ConfigMapsRouteParams> {
export interface ConfigMapsProps extends RouteComponentProps<ConfigMapsRouteParams> {
}
@observer
export class ConfigMaps extends React.Component<Props> {
export class ConfigMaps extends React.Component<ConfigMapsProps> {
render() {
return (
<KubeObjectListLayout

View File

@ -14,11 +14,11 @@ import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<PodDisruptionBudget> {
export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps<PodDisruptionBudget> {
}
@observer
export class PodDisruptionBudgetDetails extends React.Component<Props> {
export class PodDisruptionBudgetDetails extends React.Component<PodDisruptionBudgetDetailsProps> {
render() {
const { object: pdb } = this.props;

View File

@ -23,11 +23,11 @@ enum columnId {
age = "age",
}
interface Props extends KubeObjectDetailsProps<PodDisruptionBudget> {
export interface PodDisruptionBudgetsProps extends KubeObjectDetailsProps<PodDisruptionBudget> {
}
@observer
export class PodDisruptionBudgets extends React.Component<Props> {
export class PodDisruptionBudgets extends React.Component<PodDisruptionBudgetsProps> {
render() {
return (
<KubeObjectListLayout

View File

@ -20,7 +20,7 @@ import { Notifications } from "../notifications";
import { NamespaceSelect } from "../+namespaces/namespace-select";
import { SubTitle } from "../layout/sub-title";
interface Props extends DialogProps {
export interface AddQuotaDialogProps extends DialogProps {
}
const dialogState = observable.object({
@ -28,7 +28,7 @@ const dialogState = observable.object({
});
@observer
export class AddQuotaDialog extends React.Component<Props> {
export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
static defaultQuotas: IResourceQuotaValues = {
"limits.cpu": "",
"limits.memory": "",
@ -58,7 +58,7 @@ export class AddQuotaDialog extends React.Component<Props> {
@observable namespace = this.defaultNamespace;
@observable quotas = AddQuotaDialog.defaultQuotas;
constructor(props: Props) {
constructor(props: AddQuotaDialogProps) {
super(props);
makeObservable(this);
}

View File

@ -16,7 +16,7 @@ import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<ResourceQuota> {
export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps<ResourceQuota> {
}
function transformUnit(name: string, value: string): number {
@ -58,7 +58,7 @@ function renderQuotas(quota: ResourceQuota): JSX.Element[] {
}
@observer
export class ResourceQuotaDetails extends React.Component<Props> {
export class ResourceQuotaDetails extends React.Component<ResourceQuotaDetailsProps> {
render() {
const { object: quota } = this.props;

View File

@ -20,11 +20,11 @@ enum columnId {
age = "age",
}
interface Props extends RouteComponentProps<ResourceQuotaRouteParams> {
export interface ResourceQuotasProps extends RouteComponentProps<ResourceQuotaRouteParams> {
}
@observer
export class ResourceQuotas extends React.Component<Props> {
export class ResourceQuotas extends React.Component<ResourceQuotasProps> {
render() {
return (
<>

View File

@ -23,7 +23,7 @@ import { Notifications } from "../notifications";
import upperFirst from "lodash/upperFirst";
import { showDetails } from "../kube-detail-params";
interface Props extends Partial<DialogProps> {
export interface AddSecretDialogProps extends Partial<DialogProps> {
}
interface ISecretTemplateField {
@ -46,8 +46,8 @@ const dialogState = observable.object({
});
@observer
export class AddSecretDialog extends React.Component<Props> {
constructor(props: Props) {
export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
constructor(props: AddSecretDialogProps) {
super(props);
makeObservable(this);
}

View File

@ -20,16 +20,16 @@ import { Secret } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<Secret> {
export interface SecretDetailsProps extends KubeObjectDetailsProps<Secret> {
}
@observer
export class SecretDetails extends React.Component<Props> {
export class SecretDetails extends React.Component<SecretDetailsProps> {
@observable isSaving = false;
@observable data: { [name: string]: string } = {};
@observable revealSecret = observable.set<string>();
constructor(props: Props) {
constructor(props: SecretDetailsProps) {
super(props);
makeObservable(this);
}

View File

@ -24,11 +24,11 @@ enum columnId {
age = "age",
}
interface Props extends RouteComponentProps<SecretsRouteParams> {
export interface SecretsProps extends RouteComponentProps<SecretsRouteParams> {
}
@observer
export class Secrets extends React.Component<Props> {
export class Secrets extends React.Component<SecretsProps> {
render() {
return (
<>

View File

@ -18,11 +18,11 @@ import { KubeObjectMeta } from "../kube-object-meta";
import { MonacoEditor } from "../monaco-editor";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> {
export interface CRDDetailsProps extends KubeObjectDetailsProps<CustomResourceDefinition> {
}
@observer
export class CRDDetails extends React.Component<Props> {
export class CRDDetails extends React.Component<CRDDetailsProps> {
render() {
const { object: crd } = this.props;

View File

@ -32,7 +32,7 @@ enum columnId {
}
@observer
export class CrdList extends React.Component {
export class CustomResourceDefinitions extends React.Component {
constructor(props: {}) {
super(props);
makeObservable(this);

View File

@ -19,7 +19,7 @@ import { parseJsonPath } from "../../utils/jsonPath";
import { KubeObject, KubeObjectMetadata, KubeObjectStatus } from "../../../common/k8s-api/kube-object";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<KubeObject> {
export interface CustomResourceDetailsProps extends KubeObjectDetailsProps<KubeObject> {
crd: CustomResourceDefinition;
}
@ -44,7 +44,7 @@ function convertSpecValue(value: any): any {
}
@observer
export class CrdResourceDetails extends React.Component<Props> {
export class CustomResourceDetails extends React.Component<CustomResourceDetailsProps> {
renderAdditionalColumns(resource: KubeObject, columns: AdditionalPrinterColumnsV1[]) {
return columns.map(({ name, jsonPath: jp }) => (
<DrawerItem key={name} name={name} renderBoolean>

View File

@ -18,7 +18,7 @@ import { apiManager } from "../../../common/k8s-api/api-manager";
import { parseJsonPath } from "../../utils/jsonPath";
import type { CRDRouteParams } from "../../../common/routes";
interface Props extends RouteComponentProps<CRDRouteParams> {
export interface CustomResourceDefinitionResourcesProps extends RouteComponentProps<CRDRouteParams> {
}
enum columnId {
@ -28,8 +28,8 @@ enum columnId {
}
@observer
export class CrdResources extends React.Component<Props> {
constructor(props: Props) {
export class CustomResourceDefinitionResources extends React.Component<CustomResourceDefinitionResourcesProps> {
constructor(props: CustomResourceDefinitionResourcesProps) {
super(props);
makeObservable(this);
}

View File

@ -7,13 +7,14 @@ import React from "react";
import { Redirect, Route, Switch } from "react-router";
import { TabLayout } from "../layout/tab-layout";
import { crdDefinitionsRoute, crdResourcesRoute, crdURL } from "../../../common/routes";
import { CrdList, CrdResources } from ".";
import { CustomResourceDefinitions } from "./crd-list";
import { CustomResourceDefinitionResources } from "./crd-resources";
export const CustomResourcesRoute = () => (
<TabLayout>
<Switch>
<Route component={CrdList} {...crdDefinitionsRoute} exact/>
<Route component={CrdResources} {...crdResourcesRoute}/>
<Route component={CustomResourceDefinitions} {...crdDefinitionsRoute} exact/>
<Route component={CustomResourceDefinitionResources} {...crdResourcesRoute}/>
<Redirect to={crdURL()}/>
</Switch>
</TabLayout>

View File

@ -20,14 +20,14 @@ import { SettingLayout } from "../layout/setting-layout";
import logger from "../../../common/logger";
import { Avatar } from "../avatar";
interface Props extends RouteComponentProps<EntitySettingsRouteParams> {
export interface EntitySettingsProps extends RouteComponentProps<EntitySettingsRouteParams> {
}
@observer
export class EntitySettings extends React.Component<Props> {
export class EntitySettings extends React.Component<EntitySettingsProps> {
@observable activeTab: string;
constructor(props: Props) {
constructor(props: EntitySettingsProps) {
super(props);
makeObservable(this);

View File

@ -19,11 +19,11 @@ import { getDetailsUrl } from "../kube-detail-params";
import { apiManager } from "../../../common/k8s-api/api-manager";
import logger from "../../../common/logger";
interface Props extends KubeObjectDetailsProps<KubeEvent> {
export interface EventDetailsProps extends KubeObjectDetailsProps<KubeEvent> {
}
@observer
export class EventDetails extends React.Component<Props> {
export class EventDetails extends React.Component<EventDetailsProps> {
render() {
const { object: event } = this.props;

View File

@ -34,18 +34,18 @@ enum columnId {
lastSeen = "last-seen",
}
interface Props extends Partial<KubeObjectListLayoutProps<KubeEvent>> {
export interface EventsProps extends Partial<KubeObjectListLayoutProps<KubeEvent>> {
className?: IClassName;
compact?: boolean;
compactLimit?: number;
}
const defaultProps: Partial<Props> = {
const defaultProps: Partial<EventsProps> = {
compactLimit: 10,
};
@observer
export class Events extends React.Component<Props> {
export class Events extends React.Component<EventsProps> {
static defaultProps = defaultProps as object;
now = Date.now();
@ -63,7 +63,7 @@ export class Events extends React.Component<Props> {
[columnId.lastSeen]: event => this.now - new Date(event.lastTimestamp).getTime(),
};
constructor(props: Props) {
constructor(props: EventsProps) {
super(props);
makeObservable(this);
}

View File

@ -12,17 +12,18 @@ import { eventStore } from "./event.store";
import { cssNames } from "../../utils";
import type { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
interface Props {
export interface KubeEventIconProps {
object: KubeObject;
showWarningsOnly?: boolean;
filterEvents?: (events: KubeEvent[]) => KubeEvent[];
}
const defaultProps: Partial<Props> = {
const defaultProps: Partial<KubeEventIconProps> = {
showWarningsOnly: true,
};
export class KubeEventIcon extends React.Component<Props> {
export class KubeEventIcon extends React.Component<KubeEventIconProps> {
static defaultProps = defaultProps as object;
render() {

View File

@ -17,7 +17,7 @@ import extensionInstallationStateStoreInjectable
from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
interface Props {
export interface InstallProps {
installPath: string;
supportedFormats: string[];
onChange: (path: string) => void;
@ -42,7 +42,7 @@ const installInputValidator: InputValidator = {
),
};
const NonInjectedInstall: React.FC<Dependencies & Props> = ({
const NonInjectedInstall: React.FC<Dependencies & InstallProps> = ({
installPath,
supportedFormats,
onChange,
@ -99,7 +99,7 @@ const NonInjectedInstall: React.FC<Dependencies & Props> = ({
</section>
);
export const Install = withInjectables<Dependencies, Props>(
export const Install = withInjectables<Dependencies, InstallProps>(
observer(NonInjectedInstall),
{
getProps: (di, props) => ({

View File

@ -25,7 +25,7 @@ import extensionInstallationStateStoreInjectable
from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import type { ExtensionInstallationStateStore } from "../../../extensions/extension-installation-state-store/extension-installation-state-store";
interface Props {
export interface InstalledExtensionsProps {
extensions: InstalledExtension[];
enable: (id: LensExtensionId) => void;
disable: (id: LensExtensionId) => void;
@ -45,7 +45,7 @@ function getStatus(extension: InstalledExtension) {
return extension.isEnabled ? "Enabled" : "Disabled";
}
const NonInjectedInstalledExtensions : React.FC<Dependencies & Props> = (({ extensionDiscovery, extensionInstallationStateStore, extensions, uninstall, enable, disable }) => {
const NonInjectedInstalledExtensions = observer(({ extensionDiscovery, extensionInstallationStateStore, extensions, uninstall, enable, disable }: Dependencies & InstalledExtensionsProps) => {
const filters = [
(extension: InstalledExtension) => extension.manifest.name,
(extension: InstalledExtension) => getStatus(extension),
@ -175,15 +175,10 @@ const NonInjectedInstalledExtensions : React.FC<Dependencies & Props> = (({ exte
);
});
export const InstalledExtensions = withInjectables<Dependencies, Props>(
observer(NonInjectedInstalledExtensions),
{
getProps: (di, props) => ({
extensionDiscovery: di.inject(extensionDiscoveryInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
...props,
}),
},
);
export const InstalledExtensions = withInjectables<Dependencies, InstalledExtensionsProps>(NonInjectedInstalledExtensions, {
getProps: (di, props) => ({
extensionDiscovery: di.inject(extensionDiscoveryInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
...props,
}),
});

View File

@ -7,11 +7,11 @@ import styles from "./notice.module.scss";
import React, { DOMAttributes } from "react";
import { cssNames } from "../../utils";
interface Props extends DOMAttributes<any> {
export interface NoticeProps extends DOMAttributes<any> {
className?: string;
}
export function Notice(props: Props) {
export function Notice(props: NoticeProps) {
return (
<div className={cssNames(styles.notice, props.className)}>
{props.children}

View File

@ -21,7 +21,7 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable";
import { Notifications } from "../notifications";
interface Props {
export interface HelmChartDetailsProps {
chart: HelmChart;
hideDetails(): void;
}
@ -37,14 +37,14 @@ interface Dependencies {
}
@observer
class NonInjectedHelmChartDetails extends Component<Props & Dependencies> {
class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Dependencies> {
@observable chartVersions: HelmChart[];
@observable selectedChart?: HelmChart;
@observable readme?: string;
private abortController?: AbortController;
constructor(props: Props & Dependencies) {
constructor(props: HelmChartDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
@ -197,7 +197,7 @@ class NonInjectedHelmChartDetails extends Component<Props & Dependencies> {
}
}
export const HelmChartDetails = withInjectables<Dependencies, Props>(
export const HelmChartDetails = withInjectables<Dependencies, HelmChartDetailsProps>(
NonInjectedHelmChartDetails,
{

View File

@ -24,11 +24,11 @@ enum columnId {
repo = "repo",
}
interface Props extends RouteComponentProps<HelmChartsRouteParams> {
export interface HelmChartsProps extends RouteComponentProps<HelmChartsRouteParams> {
}
@observer
export class HelmCharts extends Component<Props> {
export class HelmCharts extends Component<HelmChartsProps> {
componentDidMount() {
helmChartStore.loadAll();
}

View File

@ -34,7 +34,7 @@ import releaseDetailsInjectable from "./release-details.injectable";
import releaseValuesInjectable from "./release-values.injectable";
import userSuppliedValuesAreShownInjectable from "./user-supplied-values-are-shown.injectable";
interface Props {
export interface ReleaseDetailsProps {
hideDetails(): void;
}
@ -48,12 +48,12 @@ interface Dependencies {
}
@observer
class NonInjectedReleaseDetails extends Component<Props & Dependencies> {
class NonInjectedReleaseDetails extends Component<ReleaseDetailsProps & Dependencies> {
@observable saving = false;
private nonSavedValues: string;
constructor(props: Props & Dependencies) {
constructor(props: ReleaseDetailsProps & Dependencies) {
super(props);
makeObservable(this);
}
@ -261,7 +261,7 @@ class NonInjectedReleaseDetails extends Component<Props & Dependencies> {
}
}
export const ReleaseDetails = withInjectables<Dependencies, Props>(
export const ReleaseDetails = withInjectables<Dependencies, ReleaseDetailsProps>(
NonInjectedReleaseDetails,
{

View File

@ -14,7 +14,7 @@ import createUpgradeChartTabInjectable from "../dock/upgrade-chart/create-upgrad
import releaseRollbackDialogModelInjectable from "./release-rollback-dialog-model/release-rollback-dialog-model.injectable";
import deleteReleaseInjectable from "./delete-release/delete-release.injectable";
interface Props extends MenuActionsProps {
export interface HelmReleaseMenuProps extends MenuActionsProps {
release: HelmRelease;
hideDetails?(): void;
}
@ -25,7 +25,7 @@ interface Dependencies {
openRollbackDialog: (release: HelmRelease) => void;
}
class NonInjectedHelmReleaseMenu extends React.Component<Props & Dependencies> {
class NonInjectedHelmReleaseMenu extends React.Component<HelmReleaseMenuProps & Dependencies> {
remove = () => {
return this.props.deleteRelease(this.props.release);
};
@ -79,7 +79,7 @@ class NonInjectedHelmReleaseMenu extends React.Component<Props & Dependencies> {
}
}
export const HelmReleaseMenu = withInjectables<Dependencies, Props>(
export const HelmReleaseMenu = withInjectables<Dependencies, HelmReleaseMenuProps>(
NonInjectedHelmReleaseMenu,
{

View File

@ -20,7 +20,7 @@ import releaseRollbackDialogModelInjectable
import type { ReleaseRollbackDialogModel } from "./release-rollback-dialog-model/release-rollback-dialog-model";
import rollbackReleaseInjectable from "./rollback-release/rollback-release.injectable";
interface Props extends DialogProps {
export interface ReleaseRollbackDialogProps extends DialogProps {
}
interface Dependencies {
@ -29,12 +29,12 @@ interface Dependencies {
}
@observer
class NonInjectedReleaseRollbackDialog extends React.Component<Props & Dependencies> {
class NonInjectedReleaseRollbackDialog extends React.Component<ReleaseRollbackDialogProps & Dependencies> {
@observable isLoading = false;
@observable revision: IReleaseRevision;
@observable revisions = observable.array<IReleaseRevision>();
constructor(props: Props & Dependencies) {
constructor(props: ReleaseRollbackDialogProps & Dependencies) {
super(props);
makeObservable(this);
}
@ -114,7 +114,7 @@ class NonInjectedReleaseRollbackDialog extends React.Component<Props & Dependenc
}
}
export const ReleaseRollbackDialog = withInjectables<Dependencies, Props>(
export const ReleaseRollbackDialog = withInjectables<Dependencies, ReleaseRollbackDialogProps>(
NonInjectedReleaseRollbackDialog,
{

Some files were not shown because too many files have changed in this diff Show More