Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve how extensions can manage cluster/workspace stores #1176

Merged
merged 5 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/common/__tests__/cluster-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ describe("empty config", () => {

it("sets active cluster", () => {
clusterStore.setActive("foo");
expect(clusterStore.activeCluster.id).toBe("foo");
expect(clusterStore.active.id).toBe("foo");
})
})

describe("with prod and dev clusters added", () => {
beforeEach(() => {
clusterStore.addCluster(
clusterStore.addClusters(
new Cluster({
id: "prod",
contextName: "prod",
Expand Down
44 changes: 22 additions & 22 deletions src/common/__tests__/workspace-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jest.mock("electron", () => {
}
})

import { WorkspaceStore } from "../workspace-store"
import { Workspace, WorkspaceStore } from "../workspace-store"

describe("workspace store tests", () => {
describe("for an empty config", () => {
Expand All @@ -35,27 +35,27 @@ describe("workspace store tests", () => {
it("cannot remove the default workspace", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

expect(() => ws.removeWorkspace(WorkspaceStore.defaultId)).toThrowError("Cannot remove");
expect(() => ws.removeWorkspaceById(WorkspaceStore.defaultId)).toThrowError("Cannot remove");
})

it("can update default workspace name", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: WorkspaceStore.defaultId,
name: "foobar",
});
}));

expect(ws.currentWorkspace.name).toBe("foobar");
})

it("can add workspaces", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "123",
name: "foobar",
});
}));

expect(ws.getById("123").name).toBe("foobar");
})
Expand All @@ -69,70 +69,70 @@ describe("workspace store tests", () => {
it("can set a existent workspace to be active", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "abc",
name: "foobar",
});
}));

expect(() => ws.setActive("abc")).not.toThrowError();
})

it("can remove a workspace", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "123",
name: "foobar",
});
ws.saveWorkspace({
}));
ws.addWorkspace(new Workspace({
id: "1234",
name: "foobar 1",
});
ws.removeWorkspace("123");
}));
ws.removeWorkspaceById("123");

expect(ws.workspaces.size).toBe(2);
})

it("cannot create workspace with existent name", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "someid",
name: "default",
});
}));

expect(ws.workspacesList.length).toBe(1); // default workspace only
})

it("cannot create workspace with empty name", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "random",
name: "",
});
}));

expect(ws.workspacesList.length).toBe(1); // default workspace only
})

it("cannot create workspace with ' ' name", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "random",
name: " ",
});
}));

expect(ws.workspacesList.length).toBe(1); // default workspace only
})

it("trim workspace name", () => {
const ws = WorkspaceStore.getInstance<WorkspaceStore>();

ws.saveWorkspace({
ws.addWorkspace(new Workspace({
id: "random",
name: "default ",
});
}));

expect(ws.workspacesList.length).toBe(1); // default workspace only
})
Expand Down Expand Up @@ -169,4 +169,4 @@ describe("workspace store tests", () => {
expect(ws.currentWorkspaceId).toBe("abc");
})
})
})
})
83 changes: 63 additions & 20 deletions src/common/cluster-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ export type ClusterId = string;

export interface ClusterModel {
id: ClusterId;
kubeConfigPath: string;
workspace?: WorkspaceId;
contextName?: string;
preferences?: ClusterPreferences;
metadata?: ClusterMetadata;
kubeConfigPath: string;
ownerRef?: string;

/** @deprecated */
kubeConfig?: string; // yaml
Expand Down Expand Up @@ -72,25 +73,34 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
return filePath;
}

@observable activeCluster: ClusterId;
@observable removedClusters = observable.map<ClusterId, Cluster>();
@observable clusters = observable.map<ClusterId, Cluster>();

private constructor() {
super({
configName: "lens-cluster-store",
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: migrations,
});

this.pushStateToViewsPeriodically()
}

@observable activeClusterId: ClusterId;
@observable removedClusters = observable.map<ClusterId, Cluster>();
@observable clusters = observable.map<ClusterId, Cluster>();
protected pushStateToViewsPeriodically() {
if (!ipcRenderer) {
// This is a bit of a hack, we need to do this because we might loose messages that are sent before a view is ready
setInterval(() => {
this.pushState()
}, 5000)
}
}

registerIpcListener() {
logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`)
ipcRenderer.on("cluster:state", (event, model: ClusterState) => {
this.applyWithoutSync(() => {
logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, model);
this.getById(model.id)?.updateModel(model);
})
ipcRenderer.on("cluster:state", (event, clusterId: string, state: ClusterState) => {
logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, clusterId, state);
this.getById(clusterId)?.setState(state)
})
}

Expand All @@ -99,21 +109,35 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
ipcRenderer.removeAllListeners("cluster:state")
}

@computed get activeCluster(): Cluster | null {
return this.getById(this.activeClusterId);
pushState() {
this.clusters.forEach((c) => {
c.pushState()
})
}

get activeClusterId() {
return this.activeCluster
}

@computed get clustersList(): Cluster[] {
return Array.from(this.clusters.values());
}

@computed get enabledClustersList(): Cluster[] {
return this.clustersList.filter((c) => c.enabled)
}

@computed get active(): Cluster | null {
return this.getById(this.activeCluster);
}

isActive(id: ClusterId) {
return this.activeClusterId === id;
return this.activeCluster === id;
}

@action
setActive(id: ClusterId) {
this.activeClusterId = this.clusters.has(id) ? id : null;
this.activeCluster = this.clusters.has(id) ? id : null;
}

@action
Expand Down Expand Up @@ -145,12 +169,28 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
}

@action
addCluster(...models: ClusterModel[]) {
addClusters(...models: ClusterModel[]): Cluster[] {
const clusters: Cluster[] = []
models.forEach(model => {
appEventBus.emit({name: "cluster", action: "add"})
const cluster = new Cluster(model);
this.clusters.set(model.id, cluster);
clusters.push(this.addCluster(model))
})

return clusters
}

@action
addCluster(model: ClusterModel | Cluster ): Cluster {
appEventBus.emit({name: "cluster", action: "add"})
let cluster = model as Cluster;
if (!(model instanceof Cluster)) {
cluster = new Cluster(model)
}
this.clusters.set(model.id, cluster);
return cluster
}

async removeCluster(model: ClusterModel) {
await this.removeById(model.id)
}

@action
Expand All @@ -159,7 +199,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
const cluster = this.getById(clusterId);
if (cluster) {
this.clusters.delete(clusterId);
if (this.activeClusterId === clusterId) {
if (this.activeCluster === clusterId) {
this.setActive(null);
}
// remove only custom kubeconfigs (pasted as text)
Expand Down Expand Up @@ -189,6 +229,9 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
cluster.updateModel(clusterModel);
} else {
cluster = new Cluster(clusterModel);
if (!cluster.isManaged) {
cluster.enabled = true
}
}
newClusters.set(clusterModel.id, cluster);
}
Expand All @@ -200,14 +243,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
}
});

this.activeClusterId = newClusters.has(activeCluster) ? activeCluster : null;
this.activeCluster = newClusters.has(activeCluster) ? activeCluster : null;
this.clusters.replace(newClusters);
this.removedClusters.replace(removedClusters);
}

toJSON(): ClusterStoreModel {
return toJS({
activeCluster: this.activeClusterId,
activeCluster: this.activeCluster,
clusters: this.clustersList.map(cluster => cluster.toJSON()),
}, {
recurseEverything: true
Expand Down
Loading