Merge branch 'master' into extensions-api

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen committed 2020-10-16 07:58:47 +03:00
commit a00d26149c
54 files changed
+1548 -913

No files matched your search

+3 -6
View File
@@ -6,14 +6,10 @@ import { tracker } from "./tracker";
export const clusterIpc = {
activate: createIpcChannel({
channel: "cluster:activate",
handle: (clusterId: ClusterId, frameId?: number) => {
handle: (clusterId: ClusterId, force = false) => {
const cluster = clusterStore.getById(clusterId);
if (cluster) {
if (frameId) {
cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates
}
extensionLoader.broadcastExtensions(frameId)
return cluster.activate();
return cluster.activate(force);
}
},
}),
@@ -24,6 +20,7 @@ export const clusterIpc = {
const cluster = clusterStore.getById(clusterId);
if (cluster) {
if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates
extensionLoader.broadcastExtensions(frameId)
return cluster.pushState();
}
},
+12
View File
@@ -0,0 +1,12 @@
export class ExecValidationNotFoundError extends Error {
constructor(execPath: string, isAbsolute: boolean) {
super(`User Exec command "${execPath}" not found on host.`);
let message = `User Exec command "${execPath}" not found on host.`;
if (!isAbsolute) {
message += ` Please ensure binary is found in PATH or use absolute path to binary in Kubeconfig`;
}
this.message = message;
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
+27
View File
@@ -4,6 +4,8 @@ import path from "path"
import os from "os"
import yaml from "js-yaml"
import logger from "../main/logger";
import commandExists from "command-exists";
import { ExecValidationNotFoundError } from "./custom-errors";
export const kubeConfigDefaultPath = path.join(os.homedir(), '.kube', 'config');
@@ -140,3 +142,28 @@ export function getNodeWarningConditions(node: V1Node) {
c.status.toLowerCase() === "true" && c.type !== "Ready" && c.type !== "HostUpgrades"
)
}
/**
* Validates kubeconfig supplied in the add clusters screen. At present this will just validate
* the User struct, specifically the command passed to the exec substructure.
*/
export function validateKubeConfig (config: KubeConfig) {
// we only receive a single context, cluster & user object here so lets validate them as this
// will be called when we add a new cluster to Lens
logger.debug(`validateKubeConfig: validating kubeconfig - ${JSON.stringify(config)}`);
// Validate the User Object
const user = config.getCurrentUser();
if (user.exec) {
const execCommand = user.exec["command"];
// check if the command is absolute or not
const isAbsolute = path.isAbsolute(execCommand);
// validate the exec struct in the user object, start with the command field
logger.debug(`validateKubeConfig: validating user exec command - ${JSON.stringify(execCommand)}`);
if (!commandExists.sync(execCommand)) {
logger.debug(`validateKubeConfig: exec command ${String(execCommand)} in kubeconfig ${config.currentContext} not found`);
throw new ExecValidationNotFoundError(execCommand, isAbsolute);
}
}
}
+44
View File
@@ -0,0 +1,44 @@
// Decorator for binding class methods
// Can be applied to class or single method as @autobind()
type Constructor<T = {}> = new (...args: any[]) => T;
export function autobind() {
return function (target: Constructor | object, prop?: string, descriptor?: PropertyDescriptor) {
if (target instanceof Function) return bindClass(target);
else return bindMethod(target, prop, descriptor);
}
}
function bindClass<T extends Constructor>(constructor: T) {
const proto = constructor.prototype;
const descriptors = Object.getOwnPropertyDescriptors(proto);
const skipMethod = (methodName: string) => {
return methodName === "constructor"
|| typeof descriptors[methodName].value !== "function";
};
Object.keys(descriptors).forEach(prop => {
if (skipMethod(prop)) return;
const boundDescriptor = bindMethod(proto, prop, descriptors[prop]);
Object.defineProperty(proto, prop, boundDescriptor);
})
}
function bindMethod(target: object, prop?: string, descriptor?: PropertyDescriptor) {
if (!descriptor || typeof descriptor.value !== "function") {
throw new Error(`@autobind() must be used on class or method only`)
}
const { value: func, enumerable, configurable } = descriptor;
const boundFunc = new WeakMap<object, Function>();
return Object.defineProperty(target, prop, {
enumerable,
configurable,
get() {
if (this === target) return func; // direct access from prototype
if (!boundFunc.has(this)) boundFunc.set(this, func.bind(this));
return boundFunc.get(this);
}
});
}
+9
View File
@@ -0,0 +1,9 @@
// Debouncing promise evaluation
export function debouncePromise<T, F extends any[]>(func: (...args: F) => T | Promise<T>, timeout = 0): (...args: F) => Promise<T> {
let timer: NodeJS.Timeout;
return (...params: any[]) => new Promise((resolve, reject) => {
clearTimeout(timer);
timer = setTimeout(() => resolve(func.apply(this, params)), timeout);
});
}
+9 -2
View File
@@ -1,7 +1,14 @@
// Common utils (main/renderer)
// Common utils (main OR renderer)
export * from "./app-version"
export * from "./autobind"
export * from "./base64"
export * from "./camelCase"
export * from "./cloneJson"
export * from "./debouncePromise"
export * from "./defineGlobal"
export * from "./getRandId"
export * from "./splitArray"
export * from "./getRandId"
export * from "./saveToAppFiles"
export * from "./singleton"
export * from "./cloneJson"