mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'lens-k8s-proxy-tls' of github.com:lensapp/lens into lens-k8s-proxy-tls
This commit is contained in:
commit
fb72150214
23
.eslintrc.js
23
.eslintrc.js
@ -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", {
|
||||
|
||||
15
Makefile
15
Makefile
@ -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
235
build/download_binaries.ts
Normal 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));
|
||||
@ -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();
|
||||
}
|
||||
@ -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")));
|
||||
});
|
||||
@ -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")));
|
||||
});
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
2424
extensions/kube-object-event-status/package-lock.json
generated
2424
extensions/kube-object-event-status/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
5234
extensions/metrics-cluster-feature/package-lock.json
generated
5234
extensions/metrics-cluster-feature/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
5205
extensions/node-menu/package-lock.json
generated
5205
extensions/node-menu/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
5205
extensions/pod-menu/package-lock.json
generated
5205
extensions/pod-menu/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
50
package.json
50
package.json
@ -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",
|
||||
|
||||
@ -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", () => {
|
||||
|
||||
@ -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]>();
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
8
src/common/ipc/native-theme.ts
Normal file
8
src/common/ipc/native-theme.ts
Normal 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";
|
||||
@ -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: "",
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
35
src/common/utils/lazy-initialized.ts
Normal file
35
src/common/utils/lazy-initialized.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -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>();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]>;
|
||||
@ -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();
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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[] = [];
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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 };
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
23
src/main/helm/exec.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
});
|
||||
};
|
||||
|
||||
@ -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)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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,/,}"`,
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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
18
src/main/native-theme.ts
Normal 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";
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 />;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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", () => ({
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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) => ({
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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,
|
||||
|
||||
{
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
{
|
||||
|
||||
@ -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,
|
||||
|
||||
{
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user