mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add eslint rule padding-line-between-statements (#1593)
Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
This commit is contained in:
1 parent
7b77f18376
commit
dcf253e7d5
401 files changed
+2018
-40
No files matched your search
@@ -31,8 +31,10 @@ describe("empty config", () => {
|
||||
"lens-cluster-store.json": JSON.stringify({})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -59,6 +61,7 @@ describe("empty config", () => {
|
||||
|
||||
it("adds new cluster to store", async () => {
|
||||
const storedCluster = clusterStore.getById("foo");
|
||||
|
||||
expect(storedCluster.id).toBe("foo");
|
||||
expect(storedCluster.preferences.terminalCWD).toBe("/tmp");
|
||||
expect(storedCluster.preferences.icon).toBe("data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5");
|
||||
@@ -67,6 +70,7 @@ describe("empty config", () => {
|
||||
|
||||
it("adds cluster to default workspace", () => {
|
||||
const storedCluster = clusterStore.getById("foo");
|
||||
|
||||
expect(storedCluster.workspace).toBe("default");
|
||||
});
|
||||
|
||||
@@ -114,6 +118,7 @@ describe("empty config", () => {
|
||||
it("gets clusters by workspaces", () => {
|
||||
const wsClusters = clusterStore.getByWorkspaceId("workstation");
|
||||
const defaultClusters = clusterStore.getByWorkspaceId("default");
|
||||
|
||||
expect(defaultClusters.length).toBe(0);
|
||||
expect(wsClusters.length).toBe(2);
|
||||
expect(wsClusters[0].id).toBe("prod");
|
||||
@@ -122,6 +127,7 @@ describe("empty config", () => {
|
||||
|
||||
it("check if cluster's kubeconfig file saved", () => {
|
||||
const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig");
|
||||
|
||||
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
|
||||
});
|
||||
|
||||
@@ -129,6 +135,7 @@ describe("empty config", () => {
|
||||
clusterStore.swapIconOrders("workstation", 1, 1);
|
||||
|
||||
const clusters = clusterStore.getByWorkspaceId("workstation");
|
||||
|
||||
expect(clusters[0].id).toBe("prod");
|
||||
expect(clusters[0].preferences.iconOrder).toBe(0);
|
||||
expect(clusters[1].id).toBe("dev");
|
||||
@@ -139,6 +146,7 @@ describe("empty config", () => {
|
||||
clusterStore.swapIconOrders("workstation", 0, 1);
|
||||
|
||||
const clusters = clusterStore.getByWorkspaceId("workstation");
|
||||
|
||||
expect(clusters[0].id).toBe("dev");
|
||||
expect(clusters[0].preferences.iconOrder).toBe(0);
|
||||
expect(clusters[1].id).toBe("prod");
|
||||
@@ -192,8 +200,10 @@ describe("config with existing clusters", () => {
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -203,6 +213,7 @@ describe("config with existing clusters", () => {
|
||||
|
||||
it("allows to retrieve a cluster", () => {
|
||||
const storedCluster = clusterStore.getById("cluster1");
|
||||
|
||||
expect(storedCluster.id).toBe("cluster1");
|
||||
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
|
||||
});
|
||||
@@ -210,13 +221,16 @@ describe("config with existing clusters", () => {
|
||||
it("allows to delete a cluster", () => {
|
||||
clusterStore.removeById("cluster2");
|
||||
const storedCluster = clusterStore.getById("cluster1");
|
||||
|
||||
expect(storedCluster).toBeTruthy();
|
||||
const storedCluster2 = clusterStore.getById("cluster2");
|
||||
|
||||
expect(storedCluster2).toBeUndefined();
|
||||
});
|
||||
|
||||
it("allows getting all of the clusters", async () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
|
||||
expect(storedClusters.length).toBe(3);
|
||||
expect(storedClusters[0].id).toBe("cluster1");
|
||||
expect(storedClusters[0].preferences.terminalCWD).toBe("/foo");
|
||||
@@ -227,6 +241,7 @@ describe("config with existing clusters", () => {
|
||||
|
||||
it("marks owned cluster disabled by default", () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
|
||||
expect(storedClusters[0].enabled).toBe(true);
|
||||
expect(storedClusters[2].enabled).toBe(false);
|
||||
});
|
||||
@@ -247,8 +262,10 @@ describe("pre 2.0 config with an existing cluster", () => {
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -258,6 +275,7 @@ describe("pre 2.0 config with an existing cluster", () => {
|
||||
|
||||
it("migrates to modern format with kubeconfig in a file", async () => {
|
||||
const config = clusterStore.clustersList[0].kubeConfigPath;
|
||||
|
||||
expect(fs.readFileSync(config, "utf8")).toBe("kubeconfig content");
|
||||
});
|
||||
});
|
||||
@@ -279,8 +297,10 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -292,6 +312,7 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
|
||||
const file = clusterStore.clustersList[0].kubeConfigPath;
|
||||
const config = fs.readFileSync(file, "utf8");
|
||||
const kc = yaml.safeLoad(config);
|
||||
|
||||
expect(kc.users[0].user["auth-provider"].config["access-token"]).toBe("should be string");
|
||||
expect(kc.users[0].user["auth-provider"].config["expiry"]).toBe("should be string");
|
||||
});
|
||||
@@ -319,8 +340,10 @@ describe("pre 2.6.0 config with a cluster icon", () => {
|
||||
"icon_path": testDataIcon,
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -330,6 +353,7 @@ describe("pre 2.6.0 config with a cluster icon", () => {
|
||||
|
||||
it("moves the icon into preferences", async () => {
|
||||
const storedClusterData = clusterStore.clustersList[0];
|
||||
|
||||
expect(storedClusterData.hasOwnProperty("icon")).toBe(false);
|
||||
expect(storedClusterData.preferences.hasOwnProperty("icon")).toBe(true);
|
||||
expect(storedClusterData.preferences.icon.startsWith("data:;base64,")).toBe(true);
|
||||
@@ -356,8 +380,10 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -367,6 +393,7 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
|
||||
|
||||
it("adds cluster to default workspace", async () => {
|
||||
const storedClusterData = clusterStore.clustersList[0];
|
||||
|
||||
expect(storedClusterData.workspace).toBe("default");
|
||||
});
|
||||
});
|
||||
@@ -396,8 +423,10 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
|
||||
"icon_path": testDataIcon,
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
});
|
||||
|
||||
@@ -407,11 +436,13 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
|
||||
|
||||
it("migrates to modern format with kubeconfig in a file", async () => {
|
||||
const config = clusterStore.clustersList[0].kubeConfigPath;
|
||||
|
||||
expect(fs.readFileSync(config, "utf8")).toBe("kubeconfig content");
|
||||
});
|
||||
|
||||
it("migrates to modern format with icon not in file", async () => {
|
||||
const { icon } = clusterStore.clustersList[0].preferences;
|
||||
|
||||
expect(icon.startsWith("data:;base64,")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,7 @@ describe("event bus tests", () => {
|
||||
describe("emit", () => {
|
||||
it("emits an event", () => {
|
||||
let event: AppEvent = null;
|
||||
|
||||
appEventBus.addListener((data) => {
|
||||
event = data;
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { SearchStore } from "../search-store";
|
||||
|
||||
let searchStore: SearchStore = null;
|
||||
|
||||
const logs = [
|
||||
"1:M 30 Oct 2020 16:17:41.553 # Connection with replica 172.17.0.12:6379 lost",
|
||||
"1:M 30 Oct 2020 16:17:41.623 * Replica 172.17.0.12:6379 asks for synchronization",
|
||||
@@ -64,6 +63,7 @@ describe("search store tests", () => {
|
||||
|
||||
it("escapes string for using in regex", () => {
|
||||
const regex = searchStore.escapeRegex("some.interesting-query\\#?()[]");
|
||||
|
||||
expect(regex).toBe("some\\.interesting\\-query\\\\\\#\\?\\(\\)\\[\\]");
|
||||
});
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ describe("user store tests", () => {
|
||||
|
||||
it("correctly resets theme to default value", async () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
|
||||
us.isLoaded = true;
|
||||
|
||||
us.preferences.colorTheme = "some other theme";
|
||||
|
||||
@@ -44,7 +44,6 @@ describe("workspace store tests", () => {
|
||||
|
||||
it("can update workspace description", () => {
|
||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||
|
||||
const workspace = ws.addWorkspace(new Workspace({
|
||||
id: "foobar",
|
||||
name: "foobar",
|
||||
@@ -65,6 +64,7 @@ describe("workspace store tests", () => {
|
||||
}));
|
||||
|
||||
const workspace = ws.getById("123");
|
||||
|
||||
expect(workspace.name).toBe("foobar");
|
||||
expect(workspace.enabled).toBe(true);
|
||||
});
|
||||
|
||||
@@ -55,6 +55,7 @@ export abstract class BaseStore<T = any> extends Singleton {
|
||||
if (this.params.autoLoad) {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
if (this.params.syncEnabled) {
|
||||
await this.whenLoaded;
|
||||
this.enableSync();
|
||||
@@ -63,6 +64,7 @@ export abstract class BaseStore<T = any> extends Singleton {
|
||||
|
||||
async load() {
|
||||
const { autoLoad, syncEnabled, ...confOptions } = this.params;
|
||||
|
||||
this.storeConfig = new Config({
|
||||
...confOptions,
|
||||
projectName: "lens",
|
||||
@@ -90,19 +92,23 @@ export abstract class BaseStore<T = any> extends Singleton {
|
||||
this.syncDisposers.push(
|
||||
reaction(() => this.toJSON(), model => this.onModelChange(model), this.params.syncOptions),
|
||||
);
|
||||
|
||||
if (ipcMain) {
|
||||
const callback = (event: IpcMainEvent, model: T) => {
|
||||
logger.silly(`[STORE]: SYNC ${this.name} from renderer`, { model });
|
||||
this.onSync(model);
|
||||
};
|
||||
|
||||
subscribeToBroadcast(this.syncMainChannel, callback);
|
||||
this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncMainChannel, callback));
|
||||
}
|
||||
|
||||
if (ipcRenderer) {
|
||||
const callback = (event: IpcRendererEvent, model: T) => {
|
||||
logger.silly(`[STORE]: SYNC ${this.name} from main`, { model });
|
||||
this.onSyncFromMain(model);
|
||||
};
|
||||
|
||||
subscribeToBroadcast(this.syncRendererChannel, callback);
|
||||
this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncRendererChannel, callback));
|
||||
}
|
||||
@@ -127,6 +133,7 @@ export abstract class BaseStore<T = any> extends Singleton {
|
||||
protected applyWithoutSync(callback: () => void) {
|
||||
this.disableSync();
|
||||
runInAction(callback);
|
||||
|
||||
if (this.params.syncEnabled) {
|
||||
this.enableSync();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all";
|
||||
if (ipcMain) {
|
||||
handleRequest(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
return cluster.activate(force);
|
||||
}
|
||||
@@ -22,20 +23,24 @@ if (ipcMain) {
|
||||
|
||||
handleRequest(clusterSetFrameIdHandler, (event, clusterId: ClusterId, frameId: number) => {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
clusterFrameMap.set(cluster.id, frameId);
|
||||
|
||||
return cluster.pushState();
|
||||
}
|
||||
});
|
||||
|
||||
handleRequest(clusterRefreshHandler, (event, clusterId: ClusterId) => {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
|
||||
if (cluster) return cluster.refresh({ refreshMetadata: true });
|
||||
});
|
||||
|
||||
handleRequest(clusterDisconnectHandler, (event, clusterId: ClusterId) => {
|
||||
appEventBus.emit({name: "cluster", action: "stop"});
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
cluster.disconnect();
|
||||
clusterFrameMap.delete(cluster.id);
|
||||
@@ -45,8 +50,10 @@ if (ipcMain) {
|
||||
handleRequest(clusterKubectlApplyAllHandler, (event, clusterId: ClusterId, resources: string[]) => {
|
||||
appEventBus.emit({name: "cluster", action: "kubectl-apply-all"});
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
const applier = new ResourceApplier(cluster);
|
||||
|
||||
applier.kubectlApplyAll(resources);
|
||||
} else {
|
||||
throw `${clusterId} is not a valid cluster id`;
|
||||
|
||||
@@ -98,7 +98,9 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
static embedCustomKubeConfig(clusterId: ClusterId, kubeConfig: KubeConfig | string): string {
|
||||
const filePath = ClusterStore.getCustomKubeConfigPath(clusterId);
|
||||
const fileContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig);
|
||||
|
||||
saveToAppFiles(filePath, fileContents, { mode: 0o600 });
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@@ -127,11 +129,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
id: string;
|
||||
state: ClusterState;
|
||||
};
|
||||
|
||||
if (ipcRenderer) {
|
||||
logger.info("[CLUSTER-STORE] requesting initial state sync");
|
||||
const clusterStates: clusterStateSync[] = await requestMain(ClusterStore.stateRequestChannel);
|
||||
|
||||
clusterStates.forEach((clusterState) => {
|
||||
const cluster = this.getById(clusterState.id);
|
||||
|
||||
if (cluster) {
|
||||
cluster.setState(clusterState.state);
|
||||
}
|
||||
@@ -139,12 +144,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
} else {
|
||||
handleRequest(ClusterStore.stateRequestChannel, (): clusterStateSync[] => {
|
||||
const states: clusterStateSync[] = [];
|
||||
|
||||
this.clustersList.forEach((cluster) => {
|
||||
states.push({
|
||||
state: cluster.getState(),
|
||||
id: cluster.id
|
||||
});
|
||||
});
|
||||
|
||||
return states;
|
||||
});
|
||||
}
|
||||
@@ -207,6 +214,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
@action
|
||||
setActive(id: ClusterId) {
|
||||
const clusterId = this.clusters.has(id) ? id : null;
|
||||
|
||||
this.activeCluster = clusterId;
|
||||
workspaceStore.setLastActiveClusterId(clusterId);
|
||||
}
|
||||
@@ -214,11 +222,13 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
@action
|
||||
swapIconOrders(workspace: WorkspaceId, from: number, to: number) {
|
||||
const clusters = this.getByWorkspaceId(workspace);
|
||||
|
||||
if (from < 0 || to < 0 || from >= clusters.length || to >= clusters.length || isNaN(from) || isNaN(to)) {
|
||||
throw new Error(`invalid from<->to arguments`);
|
||||
}
|
||||
|
||||
move.mutate(clusters, from, to);
|
||||
|
||||
for (const i in clusters) {
|
||||
// This resets the iconOrder to the current display order
|
||||
clusters[i].preferences.iconOrder = +i;
|
||||
@@ -236,12 +246,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
getByWorkspaceId(workspaceId: string): Cluster[] {
|
||||
const clusters = Array.from(this.clusters.values())
|
||||
.filter(cluster => cluster.workspace === workspaceId);
|
||||
|
||||
return _.sortBy(clusters, cluster => cluster.preferences.iconOrder);
|
||||
}
|
||||
|
||||
@action
|
||||
addClusters(...models: ClusterModel[]): Cluster[] {
|
||||
const clusters: Cluster[] = [];
|
||||
|
||||
models.forEach(model => {
|
||||
clusters.push(this.addCluster(model));
|
||||
});
|
||||
@@ -253,13 +265,16 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
addCluster(model: ClusterModel | Cluster): Cluster {
|
||||
appEventBus.emit({ name: "cluster", action: "add" });
|
||||
let cluster = model as Cluster;
|
||||
|
||||
if (!(model instanceof Cluster)) {
|
||||
cluster = new Cluster(model);
|
||||
}
|
||||
|
||||
if (!cluster.isManaged) {
|
||||
cluster.enabled = true;
|
||||
}
|
||||
this.clusters.set(model.id, cluster);
|
||||
|
||||
return cluster;
|
||||
}
|
||||
|
||||
@@ -271,11 +286,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
async removeById(clusterId: ClusterId) {
|
||||
appEventBus.emit({ name: "cluster", action: "remove" });
|
||||
const cluster = this.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
this.clusters.delete(clusterId);
|
||||
|
||||
if (this.activeCluster === clusterId) {
|
||||
this.setActive(null);
|
||||
}
|
||||
|
||||
// remove only custom kubeconfigs (pasted as text)
|
||||
if (cluster.kubeConfigPath == ClusterStore.getCustomKubeConfigPath(clusterId)) {
|
||||
unlink(cluster.kubeConfigPath).catch(() => null);
|
||||
@@ -299,10 +317,12 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
// update new clusters
|
||||
for (const clusterModel of clusters) {
|
||||
let cluster = currentClusters.get(clusterModel.id);
|
||||
|
||||
if (cluster) {
|
||||
cluster.updateModel(clusterModel);
|
||||
} else {
|
||||
cluster = new Cluster(clusterModel);
|
||||
|
||||
if (!cluster.isManaged) {
|
||||
cluster.enabled = true;
|
||||
}
|
||||
@@ -336,6 +356,7 @@ export const clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
export function getClusterIdFromHost(hostname: string): ClusterId {
|
||||
const subDomains = hostname.split(":")[0].split(".");
|
||||
|
||||
return subDomains.slice(-2)[0]; // e.g host == "%clusterId.localhost:45345"
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ 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`;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export class EventEmitter<D extends [...any[]]> {
|
||||
addListener(callback: Callback<D>, options: Options = {}) {
|
||||
if (options.prepend) {
|
||||
const listeners = [...this.listeners];
|
||||
|
||||
listeners.unshift([callback, options]);
|
||||
this.listeners = new Map(listeners);
|
||||
}
|
||||
@@ -33,7 +34,9 @@ export class EventEmitter<D extends [...any[]]> {
|
||||
[...this.listeners].every(([callback, options]) => {
|
||||
if (options.once) this.removeListener(callback);
|
||||
const result = callback(...data);
|
||||
|
||||
if (result === false) return; // break cycle
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,18 +16,22 @@ export async function requestMain(channel: string, ...args: any[]) {
|
||||
|
||||
async function getSubFrames(): Promise<number[]> {
|
||||
const subFrames: number[] = [];
|
||||
|
||||
clusterFrameMap.forEach(frameId => {
|
||||
subFrames.push(frameId);
|
||||
});
|
||||
|
||||
return subFrames;
|
||||
}
|
||||
|
||||
export function broadcastMessage(channel: string, ...args: any[]) {
|
||||
const views = (webContents || remote?.webContents)?.getAllWebContents();
|
||||
|
||||
if (!views) return;
|
||||
|
||||
views.forEach(webContent => {
|
||||
const type = webContent.getType();
|
||||
|
||||
logger.silly(`[IPC]: broadcasting "${channel}" to ${type}=${webContent.id}`, { args });
|
||||
webContent.send(channel, ...args);
|
||||
getSubFrames().then((frames) => {
|
||||
@@ -36,6 +40,7 @@ export function broadcastMessage(channel: string, ...args: any[]) {
|
||||
});
|
||||
}).catch((e) => e);
|
||||
});
|
||||
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send(channel, ...args);
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@ function resolveTilde(filePath: string) {
|
||||
if (filePath[0] === "~" && (filePath[1] === "/" || filePath.length === 1)) {
|
||||
return filePath.replace("~", os.homedir());
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@@ -40,12 +41,15 @@ export function validateConfig(config: KubeConfig | string): KubeConfig {
|
||||
config = loadConfig(config);
|
||||
}
|
||||
logger.debug(`validating kube config: ${JSON.stringify(config)}`);
|
||||
|
||||
if (!config.users || config.users.length == 0) {
|
||||
throw new Error("No users provided in config");
|
||||
}
|
||||
|
||||
if (!config.clusters || config.clusters.length == 0) {
|
||||
throw new Error("No clusters provided in config");
|
||||
}
|
||||
|
||||
if (!config.contexts || config.contexts.length == 0) {
|
||||
throw new Error("No contexts provided in config");
|
||||
}
|
||||
@@ -58,11 +62,13 @@ export function validateConfig(config: KubeConfig | string): KubeConfig {
|
||||
*/
|
||||
export function splitConfig(kubeConfig: KubeConfig): KubeConfig[] {
|
||||
const configs: KubeConfig[] = [];
|
||||
|
||||
if (!kubeConfig.contexts) {
|
||||
return configs;
|
||||
}
|
||||
kubeConfig.contexts.forEach(ctx => {
|
||||
const kc = new KubeConfig();
|
||||
|
||||
kc.clusters = [kubeConfig.getCluster(ctx.cluster)].filter(n => n);
|
||||
kc.users = [kubeConfig.getUser(ctx.user)].filter(n => n);
|
||||
kc.contexts = [kubeConfig.getContextObject(ctx.name)].filter(n => n);
|
||||
@@ -70,6 +76,7 @@ export function splitConfig(kubeConfig: KubeConfig): KubeConfig[] {
|
||||
|
||||
configs.push(kc);
|
||||
});
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
@@ -153,11 +160,13 @@ export function validateKubeConfig (config: KubeConfig) {
|
||||
logger.debug(`validateKubeConfig: validating kubeconfig - ${JSON.stringify(config)}`);
|
||||
|
||||
// Validate the User Object
|
||||
const user = config.getCurrentUser();
|
||||
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)}`);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PrometheusProviderRegistry } from "../main/prometheus/provider-registry
|
||||
|
||||
[PrometheusLens, PrometheusHelm, PrometheusOperator, PrometheusStacklight].forEach(providerClass => {
|
||||
const provider = new providerClass();
|
||||
|
||||
PrometheusProviderRegistry.registerProvider(provider.id, provider);
|
||||
});
|
||||
|
||||
|
||||
@@ -42,10 +42,12 @@ export function isAllowedResource(resources: KubeResource | KubeResource[]) {
|
||||
resources = [resources];
|
||||
}
|
||||
const { allowedResources = [] } = getHostedCluster() || {};
|
||||
|
||||
for (const resource of resources) {
|
||||
if (!allowedResources.includes(resource)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export function registerFileProtocol(name: string, basePath: string) {
|
||||
protocol.registerFileProtocol(name, (request, callback) => {
|
||||
const filePath = request.url.replace(`${name}://`, "");
|
||||
const absPath = path.resolve(basePath, filePath);
|
||||
|
||||
callback({ path: absPath });
|
||||
});
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { userStore } from "./user-store";
|
||||
|
||||
function getDefaultRequestOpts(): Partial<request.Options> {
|
||||
const { httpsProxy, allowUntrustedCAs } = userStore.preferences;
|
||||
|
||||
return {
|
||||
proxy: httpsProxy || undefined,
|
||||
rejectUnauthorized: !allowUntrustedCAs,
|
||||
|
||||
@@ -14,8 +14,10 @@ export class SearchStore {
|
||||
@action
|
||||
onSearch(text: string[], query = this.searchQuery) {
|
||||
this.searchQuery = query;
|
||||
|
||||
if (!query) {
|
||||
this.reset();
|
||||
|
||||
return;
|
||||
}
|
||||
this.occurrences = this.findOccurences(text, query);
|
||||
@@ -36,11 +38,14 @@ export class SearchStore {
|
||||
findOccurences(text: string[], query: string) {
|
||||
if (!text) return [];
|
||||
const occurences: number[] = [];
|
||||
|
||||
text.forEach((line, index) => {
|
||||
const regex = new RegExp(this.escapeRegex(query), "gi");
|
||||
const matches = [...line.matchAll(regex)];
|
||||
|
||||
matches.forEach(() => occurences.push(index));
|
||||
});
|
||||
|
||||
return occurences;
|
||||
}
|
||||
|
||||
@@ -51,9 +56,11 @@ export class SearchStore {
|
||||
*/
|
||||
getNextOverlay(loopOver = false) {
|
||||
const next = this.activeOverlayIndex + 1;
|
||||
|
||||
if (next > this.occurrences.length - 1) {
|
||||
return loopOver ? 0 : this.activeOverlayIndex;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
@@ -64,9 +71,11 @@ export class SearchStore {
|
||||
*/
|
||||
getPrevOverlay(loopOver = false) {
|
||||
const prev = this.activeOverlayIndex - 1;
|
||||
|
||||
if (prev < 0) {
|
||||
return loopOver ? this.occurrences.length - 1 : this.activeOverlayIndex;
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
@@ -104,6 +113,7 @@ export class SearchStore {
|
||||
@autobind()
|
||||
isActiveOverlay(line: number, occurence: number) {
|
||||
const firstLineIndex = this.occurrences.findIndex(item => item === line);
|
||||
|
||||
return firstLineIndex + occurence === this.activeOverlayIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import logger from "../main/logger";
|
||||
if (isMac) {
|
||||
for (const crt of macca.all()) {
|
||||
const attributes = crt.issuer?.attributes?.map((a: any) => `${a.name}=${a.value}`);
|
||||
|
||||
logger.debug(`Using host CA: ${attributes.join(",")}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isWindows) {
|
||||
winca.inject("+"); // see: https://github.com/ukoloff/win-ca#caveats
|
||||
}
|
||||
@@ -102,6 +102,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
protected refreshNewContexts = async () => {
|
||||
try {
|
||||
const kubeConfig = await readFile(this.kubeConfigPath, "utf8");
|
||||
|
||||
if (kubeConfig) {
|
||||
this.newContexts.clear();
|
||||
loadConfig(kubeConfig).getContexts()
|
||||
@@ -118,6 +119,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
@action
|
||||
markNewContextsAsSeen() {
|
||||
const { seenContexts, newContexts } = this;
|
||||
|
||||
this.seenContexts.replace([...seenContexts, ...newContexts]);
|
||||
this.newContexts.clear();
|
||||
}
|
||||
@@ -133,9 +135,11 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
@action
|
||||
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
||||
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data;
|
||||
|
||||
if (lastSeenAppVersion) {
|
||||
this.lastSeenAppVersion = lastSeenAppVersion;
|
||||
}
|
||||
|
||||
if (kubeConfigPath) {
|
||||
this.kubeConfigPath = kubeConfigPath;
|
||||
}
|
||||
@@ -150,6 +154,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
seenContexts: Array.from(this.seenContexts),
|
||||
preferences: this.preferences,
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@ export function autobind() {
|
||||
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";
|
||||
@@ -21,6 +20,7 @@ function bindClass<T extends Constructor>(constructor: T) {
|
||||
Object.keys(descriptors).forEach(prop => {
|
||||
if (skipMethod(prop)) return;
|
||||
const boundDescriptor = bindMethod(proto, prop, descriptors[prop]);
|
||||
|
||||
Object.defineProperty(proto, prop, boundDescriptor);
|
||||
});
|
||||
}
|
||||
@@ -38,6 +38,7 @@ function bindMethod(target: object, prop?: string, descriptor?: PropertyDescript
|
||||
get() {
|
||||
if (this === target) return func; // direct access from prototype
|
||||
if (!boundFunc.has(this)) boundFunc.set(this, func.bind(this));
|
||||
|
||||
return boundFunc.get(this);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,8 +7,10 @@ export interface IURLParams<P extends object = {}, Q extends object = {}> {
|
||||
|
||||
export function buildURL<P extends object = {}, Q extends object = {}>(path: string | any) {
|
||||
const pathBuilder = compile(String(path));
|
||||
|
||||
return function ({ params, query }: IURLParams<P, Q> = {}) {
|
||||
const queryParams = query ? new URLSearchParams(Object.entries(query)).toString() : "";
|
||||
|
||||
return pathBuilder(params) + (queryParams ? `?${queryParams}` : "");
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,9 @@ export function toCamelCase(obj: Record<string, any>): any {
|
||||
else if (isPlainObject(obj)) {
|
||||
return Object.keys(obj).reduce((result, key) => {
|
||||
const value = obj[key];
|
||||
|
||||
result[camelCase(key)] = typeof value === "object" ? toCamelCase(value) : value;
|
||||
|
||||
return result;
|
||||
}, {} as any);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
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 => {
|
||||
clearTimeout(timer);
|
||||
timer = global.setTimeout(() => resolve(func.apply(this, params)), timeout);
|
||||
|
||||
@@ -26,6 +26,7 @@ export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions)
|
||||
resolve(Buffer.concat(fileChunks));
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
url,
|
||||
promise,
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
export function getRandId({ prefix = "", suffix = "", sep = "_" } = {}) {
|
||||
const randId = () => Math.random().toString(16).substr(2);
|
||||
|
||||
return [prefix, randId(), suffix].filter(s => s).join(sep);
|
||||
}
|
||||
@@ -6,7 +6,9 @@ import { WriteFileOptions } from "fs";
|
||||
|
||||
export function saveToAppFiles(filePath: string, contents: any, options?: WriteFileOptions): string {
|
||||
const absPath = path.resolve((app || remote.app).getPath("userData"), filePath);
|
||||
|
||||
ensureDirSync(path.dirname(absPath));
|
||||
writeFileSync(absPath, contents, options);
|
||||
|
||||
return absPath;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ class Singleton {
|
||||
if (!Singleton.instances.has(this)) {
|
||||
Singleton.instances.set(this, Reflect.construct(this, args));
|
||||
}
|
||||
|
||||
return Singleton.instances.get(this) as T;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
*/
|
||||
export function splitArray<T>(array: T[], element: T): [T[], T[], boolean] {
|
||||
const index = array.indexOf(element);
|
||||
|
||||
if (index < 0) {
|
||||
return [array, [], false];
|
||||
}
|
||||
|
||||
return [array.slice(0, index), array.slice(index + 1, array.length), true];
|
||||
}
|
||||
@@ -26,6 +26,7 @@ export function readFileFromTar<R = Buffer>({ tarPath, filePath, parseJson }: Re
|
||||
entry.once("end", () => {
|
||||
const data = Buffer.concat(fileChunks);
|
||||
const result = parseJson ? JSON.parse(data.toString("utf8")) : data;
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
},
|
||||
@@ -39,12 +40,14 @@ export function readFileFromTar<R = Buffer>({ tarPath, filePath, parseJson }: Re
|
||||
|
||||
export async function listTarEntries(filePath: string): Promise<string[]> {
|
||||
const entries: string[] = [];
|
||||
|
||||
await tar.list({
|
||||
file: filePath,
|
||||
onentry: (entry: FileStat) => {
|
||||
entries.push(path.normalize(entry.path as any as string));
|
||||
},
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ defineGlobal("__static", {
|
||||
if (isDevelopment) {
|
||||
return path.resolve(contextDir, "static");
|
||||
}
|
||||
|
||||
return path.resolve(process.resourcesPath, "static");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -125,11 +125,14 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
id: string;
|
||||
state: WorkspaceState;
|
||||
};
|
||||
|
||||
if (ipcRenderer) {
|
||||
logger.info("[WORKSPACE-STORE] requesting initial state sync");
|
||||
const workspaceStates: workspaceStateSync[] = await requestMain(WorkspaceStore.stateRequestChannel);
|
||||
|
||||
workspaceStates.forEach((workspaceState) => {
|
||||
const workspace = this.getById(workspaceState.id);
|
||||
|
||||
if (workspace) {
|
||||
workspace.setState(workspaceState.state);
|
||||
}
|
||||
@@ -137,12 +140,14 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
} else {
|
||||
handleRequest(WorkspaceStore.stateRequestChannel, (): workspaceStateSync[] => {
|
||||
const states: workspaceStateSync[] = [];
|
||||
|
||||
this.workspacesList.forEach((workspace) => {
|
||||
states.push({
|
||||
state: workspace.getState(),
|
||||
id: workspace.id
|
||||
});
|
||||
});
|
||||
|
||||
return states;
|
||||
});
|
||||
}
|
||||
@@ -202,6 +207,7 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
@action
|
||||
setActive(id = WorkspaceStore.defaultId) {
|
||||
if (id === this.currentWorkspaceId) return;
|
||||
|
||||
if (!this.getById(id)) {
|
||||
throw new Error(`workspace ${id} doesn't exist`);
|
||||
}
|
||||
@@ -211,15 +217,18 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
@action
|
||||
addWorkspace(workspace: Workspace) {
|
||||
const { id, name } = workspace;
|
||||
|
||||
if (!name.trim() || this.getByName(name.trim())) {
|
||||
return;
|
||||
}
|
||||
this.workspaces.set(id, workspace);
|
||||
|
||||
if (!workspace.isManaged) {
|
||||
workspace.enabled = true;
|
||||
}
|
||||
|
||||
appEventBus.emit({name: "workspace", action: "add"});
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@@ -237,10 +246,13 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
@action
|
||||
removeWorkspaceById(id: WorkspaceId) {
|
||||
const workspace = this.getById(id);
|
||||
|
||||
if (!workspace) return;
|
||||
|
||||
if (this.isDefault(id)) {
|
||||
throw new Error("Cannot remove default workspace");
|
||||
}
|
||||
|
||||
if (this.currentWorkspaceId === id) {
|
||||
this.currentWorkspaceId = WorkspaceStore.defaultId; // reset to default
|
||||
}
|
||||
@@ -259,10 +271,12 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
if (currentWorkspace) {
|
||||
this.currentWorkspaceId = currentWorkspace;
|
||||
}
|
||||
|
||||
if (workspaces.length) {
|
||||
this.workspaces.clear();
|
||||
workspaces.forEach(ws => {
|
||||
const workspace = new Workspace(ws);
|
||||
|
||||
if (!workspace.isManaged) {
|
||||
workspace.enabled = true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user