mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Copy detected metadata from cluster to catalog cluster (#7316)
* Copy detected metadata from cluster to catalog cluster Signed-off-by: Juho Heikka <juho.heikka@gmail.com> * Remove duplicate copyright comment Signed-off-by: Juho Heikka <juho.heikka@gmail.com> * Lint fixes Signed-off-by: Juho Heikka <juho.heikka@gmail.com> * Typescript fix Signed-off-by: Juho Heikka <juho.heikka@gmail.com> --------- Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
This commit is contained in:
parent
48ce558420
commit
d5a124bd79
8
packages/core/src/common/utils/enum.ts
Normal file
8
packages/core/src/common/utils/enum.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 function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
|
||||
return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[];
|
||||
}
|
||||
@ -8,6 +8,8 @@ import loggerInjectable from "../../common/logger.injectable";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
||||
import clustersThatAreBeingDeletedInjectable from "./are-being-deleted.injectable";
|
||||
import { ClusterManager } from "./manager";
|
||||
import updateEntityMetadataInjectable from "./update-entity-metadata.injectable";
|
||||
import updateEntitySpecInjectable from "./update-entity-spec.injectable";
|
||||
import visibleClusterInjectable from "./visible-cluster.injectable";
|
||||
|
||||
const clusterManagerInjectable = getInjectable({
|
||||
@ -19,6 +21,8 @@ const clusterManagerInjectable = getInjectable({
|
||||
clustersThatAreBeingDeleted: di.inject(clustersThatAreBeingDeletedInjectable),
|
||||
visibleCluster: di.inject(visibleClusterInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
updateEntityMetadata: di.inject(updateEntityMetadataInjectable),
|
||||
updateEntitySpec: di.inject(updateEntitySpecInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ import type { IObservableValue, ObservableSet } from "mobx";
|
||||
import { action, makeObservable, observe, reaction, toJS } from "mobx";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { isErrnoException } from "../../common/utils";
|
||||
import type { KubernetesClusterPrometheusMetrics } from "../../common/catalog-entities/kubernetes-cluster";
|
||||
import { isKubernetesCluster, KubernetesCluster, LensKubernetesClusterStatus } from "../../common/catalog-entities/kubernetes-cluster";
|
||||
import { ipcMainOn } from "../../common/ipc";
|
||||
import { once } from "lodash";
|
||||
@ -16,6 +15,8 @@ import type { ClusterStore } from "../../common/cluster-store/cluster-store";
|
||||
import type { ClusterId } from "../../common/cluster-types";
|
||||
import type { CatalogEntityRegistry } from "../catalog";
|
||||
import type { Logger } from "../../common/logger";
|
||||
import type { UpdateEntityMetadata } from "./update-entity-metadata.injectable";
|
||||
import type { UpdateEntitySpec } from "./update-entity-spec.injectable";
|
||||
|
||||
const logPrefix = "[CLUSTER-MANAGER]:";
|
||||
|
||||
@ -27,6 +28,8 @@ interface Dependencies {
|
||||
readonly clustersThatAreBeingDeleted: ObservableSet<ClusterId>;
|
||||
readonly visibleCluster: IObservableValue<ClusterId | null>;
|
||||
readonly logger: Logger;
|
||||
readonly updateEntityMetadata: UpdateEntityMetadata;
|
||||
readonly updateEntitySpec: UpdateEntitySpec;
|
||||
}
|
||||
|
||||
export class ClusterManager {
|
||||
@ -97,42 +100,8 @@ export class ClusterManager {
|
||||
|
||||
this.updateEntityStatus(entity, cluster);
|
||||
|
||||
entity.metadata.labels = {
|
||||
...entity.metadata.labels,
|
||||
...cluster.labels,
|
||||
};
|
||||
entity.metadata.distro = cluster.distribution;
|
||||
entity.metadata.kubeVersion = cluster.version;
|
||||
|
||||
if (cluster.preferences?.clusterName) {
|
||||
/**
|
||||
* Only set the name if the it is overriden in preferences. If it isn't
|
||||
* set then the name of the entity has been explicitly set by its source
|
||||
*/
|
||||
entity.metadata.name = cluster.preferences.clusterName;
|
||||
}
|
||||
|
||||
entity.spec.metrics ||= { source: "local" };
|
||||
|
||||
if (entity.spec.metrics.source === "local") {
|
||||
const prometheus: KubernetesClusterPrometheusMetrics = entity.spec?.metrics?.prometheus || {};
|
||||
|
||||
prometheus.type = cluster.preferences.prometheusProvider?.type;
|
||||
prometheus.address = cluster.preferences.prometheus;
|
||||
entity.spec.metrics.prometheus = prometheus;
|
||||
}
|
||||
|
||||
if (cluster.preferences.icon) {
|
||||
entity.spec.icon ??= {};
|
||||
entity.spec.icon.src = cluster.preferences.icon;
|
||||
} else if (cluster.preferences.icon === null) {
|
||||
/**
|
||||
* NOTE: only clear the icon if set to `null` by ClusterIconSettings.
|
||||
* We can then also clear that value too
|
||||
*/
|
||||
entity.spec.icon = undefined;
|
||||
cluster.preferences.icon = undefined;
|
||||
}
|
||||
this.dependencies.updateEntityMetadata(entity, cluster);
|
||||
this.dependencies.updateEntitySpec(entity, cluster);
|
||||
|
||||
this.dependencies.catalogEntityRegistry.items.splice(index, 1, entity);
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { KubernetesCluster } from "../../common/catalog-entities";
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { enumKeys } from "../../common/utils/enum";
|
||||
|
||||
export type UpdateEntityMetadata = (entity: KubernetesCluster, cluster: Cluster) => void;
|
||||
|
||||
const updateEntityMetadataInjectable = getInjectable({
|
||||
id: "update-entity-metadata",
|
||||
|
||||
instantiate: (): UpdateEntityMetadata => {
|
||||
return (entity, cluster) => {
|
||||
entity.metadata.labels = {
|
||||
...entity.metadata.labels,
|
||||
...cluster.labels,
|
||||
};
|
||||
entity.metadata.distro = cluster.distribution;
|
||||
entity.metadata.kubeVersion = cluster.version;
|
||||
|
||||
enumKeys(ClusterMetadataKey).forEach((key) => {
|
||||
const metadataKey = ClusterMetadataKey[key];
|
||||
|
||||
entity.metadata[metadataKey] = cluster.metadata[metadataKey];
|
||||
});
|
||||
|
||||
if (cluster.preferences?.clusterName) {
|
||||
/**
|
||||
* Only set the name if the it is overriden in preferences. If it isn't
|
||||
* set then the name of the entity has been explicitly set by its source
|
||||
*/
|
||||
entity.metadata.name = cluster.preferences.clusterName;
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default updateEntityMetadataInjectable;
|
||||
160
packages/core/src/main/cluster/update-entity-metadata.test.ts
Normal file
160
packages/core/src/main/cluster/update-entity-metadata.test.ts
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { AppPaths } from "../../common/app-paths/app-path-injection-token";
|
||||
import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable";
|
||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { KubernetesCluster } from "../../common/catalog-entities";
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import type { UpdateEntityMetadata } from "./update-entity-metadata.injectable";
|
||||
import updateEntityMetadataInjectable from "./update-entity-metadata.injectable";
|
||||
|
||||
describe("update-entity-metadata", () => {
|
||||
let cluster: Cluster;
|
||||
let entity: KubernetesCluster;
|
||||
let updateEntityMetadata: UpdateEntityMetadata;
|
||||
let detectedMetadata: Record<ClusterMetadataKey, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting();
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||
di.override(appPathsStateInjectable, () => ({
|
||||
get: () => ({} as AppPaths),
|
||||
set: () => {},
|
||||
}));
|
||||
const createCluster = di.inject(createClusterInjectionToken);
|
||||
|
||||
updateEntityMetadata = di.inject(updateEntityMetadataInjectable);
|
||||
|
||||
cluster = createCluster({
|
||||
id: "some-id",
|
||||
contextName: "some-context",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
}, {
|
||||
clusterServerUrl: "foo",
|
||||
});
|
||||
|
||||
detectedMetadata = {
|
||||
[ClusterMetadataKey.CLUSTER_ID]: "some-cluster-id",
|
||||
[ClusterMetadataKey.DISTRIBUTION]: "some-distribution",
|
||||
[ClusterMetadataKey.VERSION]: "some-version",
|
||||
[ClusterMetadataKey.LAST_SEEN]: "some-date",
|
||||
[ClusterMetadataKey.NODES_COUNT]: 42,
|
||||
[ClusterMetadataKey.PROMETHEUS]: {
|
||||
"some-parameter": "some-value",
|
||||
},
|
||||
};
|
||||
|
||||
cluster.metadata = {
|
||||
...cluster.metadata,
|
||||
};
|
||||
|
||||
entity = new KubernetesCluster({
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
labels: {},
|
||||
},
|
||||
spec: {
|
||||
kubeconfigContext: "some-context",
|
||||
kubeconfigPath: "/some/path/to/kubeconfig",
|
||||
},
|
||||
status: {
|
||||
phase: "connecting",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("given cluster metadata has no some last seen timestamp, does not update entity metadata with last seen timestamp", () => {
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.lastSeen).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("given cluster metadata has some last seen timestamp, updates entity metadata with last seen timestamp", () => {
|
||||
cluster.metadata[ClusterMetadataKey.LAST_SEEN] = detectedMetadata[ClusterMetadataKey.LAST_SEEN];
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.lastSeen).toEqual("some-date");
|
||||
});
|
||||
|
||||
it("given cluster metadata has some version, updates entity metadata with version", () => {
|
||||
cluster.metadata[ClusterMetadataKey.VERSION] = detectedMetadata[ClusterMetadataKey.VERSION];
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.version).toEqual("some-version");
|
||||
});
|
||||
|
||||
it("given cluster metadata has nodes count, updates entity metadata with node count", () => {
|
||||
cluster.metadata[ClusterMetadataKey.NODES_COUNT] = detectedMetadata[ClusterMetadataKey.NODES_COUNT];
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.nodes).toEqual(42);
|
||||
});
|
||||
|
||||
it("given cluster metadata has prometheus data, updates entity metadata with prometheus data", () => {
|
||||
cluster.metadata[ClusterMetadataKey.PROMETHEUS] = detectedMetadata[ClusterMetadataKey.PROMETHEUS];
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.prometheus).toEqual({
|
||||
"some-parameter": "some-value",
|
||||
});
|
||||
});
|
||||
|
||||
it("given cluster metadata has distribution, updates entity metadata with distribution", () => {
|
||||
cluster.metadata[ClusterMetadataKey.DISTRIBUTION] = detectedMetadata[ClusterMetadataKey.DISTRIBUTION];
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.distribution).toEqual("some-distribution");
|
||||
});
|
||||
|
||||
it("given cluster metadata has cluster id, updates entity metadata with cluster id", () => {
|
||||
cluster.metadata[ClusterMetadataKey.CLUSTER_ID] = detectedMetadata[ClusterMetadataKey.CLUSTER_ID];
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.id).toEqual("some-cluster-id");
|
||||
});
|
||||
|
||||
it("given cluster metadata has no kubernetes version, updates entity metadata with 'unknown' kubernetes version", () => {
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.kubeVersion).toEqual("unknown");
|
||||
});
|
||||
|
||||
it("given cluster metadata has kubernetes version, updates entity metadata with kubernetes version", () => {
|
||||
cluster.metadata.version = "some-kubernetes-version";
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.kubeVersion).toEqual("some-kubernetes-version");
|
||||
});
|
||||
|
||||
it("given cluster has labels, updates entity metadata with labels", () => {
|
||||
cluster.labels = {
|
||||
"some-label": "some-value",
|
||||
};
|
||||
entity.metadata.labels = {
|
||||
"some-other-label": "some-other-value",
|
||||
};
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.labels).toEqual({
|
||||
"some-label": "some-value",
|
||||
"some-other-label": "some-other-value",
|
||||
});
|
||||
});
|
||||
|
||||
it("given cluster has labels, overwrites entity metadata with cluster labels", () => {
|
||||
cluster.labels = {
|
||||
"some-label": "some-cluster-value",
|
||||
};
|
||||
entity.metadata.labels = {
|
||||
"some-label": "some-entity-value",
|
||||
};
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.labels).toEqual({
|
||||
"some-label": "some-cluster-value",
|
||||
});
|
||||
});
|
||||
|
||||
it("give cluster preferences has name, updates entity metadata with name", () => {
|
||||
cluster.preferences.clusterName = "some-cluster-name";
|
||||
|
||||
updateEntityMetadata(entity, cluster);
|
||||
expect(entity.metadata.name).toEqual("some-cluster-name");
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../../common/catalog-entities";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
|
||||
export type UpdateEntitySpec = (entity: KubernetesCluster, cluster: Cluster) => void;
|
||||
|
||||
const updateEntitySpecInjectable = getInjectable({
|
||||
id: "update-entity-spec",
|
||||
|
||||
instantiate: (): UpdateEntitySpec => {
|
||||
return (entity, cluster) => {
|
||||
entity.spec.metrics ||= { source: "local" };
|
||||
|
||||
if (entity.spec.metrics.source === "local") {
|
||||
const prometheus: KubernetesClusterPrometheusMetrics = entity.spec?.metrics?.prometheus || {};
|
||||
|
||||
prometheus.type = cluster.preferences.prometheusProvider?.type;
|
||||
prometheus.address = cluster.preferences.prometheus;
|
||||
entity.spec.metrics.prometheus = prometheus;
|
||||
}
|
||||
|
||||
if (cluster.preferences.icon) {
|
||||
entity.spec.icon ??= {};
|
||||
entity.spec.icon.src = cluster.preferences.icon;
|
||||
} else if (cluster.preferences.icon === null) {
|
||||
/**
|
||||
* NOTE: only clear the icon if set to `null` by ClusterIconSettings.
|
||||
* We can then also clear that value too
|
||||
*/
|
||||
entity.spec.icon = undefined;
|
||||
cluster.preferences.icon = undefined;
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default updateEntitySpecInjectable;
|
||||
154
packages/core/src/main/cluster/update-entity-spec.test.ts
Normal file
154
packages/core/src/main/cluster/update-entity-spec.test.ts
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { AppPaths } from "../../common/app-paths/app-path-injection-token";
|
||||
import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable";
|
||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { KubernetesCluster } from "../../common/catalog-entities";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import type { UpdateEntitySpec } from "./update-entity-spec.injectable";
|
||||
import updateEntitySpecInjectable from "./update-entity-spec.injectable";
|
||||
|
||||
describe("update-entity-spec", () => {
|
||||
let cluster: Cluster;
|
||||
let entity: KubernetesCluster;
|
||||
let updateEntitySpec: UpdateEntitySpec;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting();
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||
di.override(appPathsStateInjectable, () => ({
|
||||
get: () => ({} as AppPaths),
|
||||
set: () => {},
|
||||
}));
|
||||
const createCluster = di.inject(createClusterInjectionToken);
|
||||
|
||||
updateEntitySpec = di.inject(updateEntitySpecInjectable);
|
||||
|
||||
cluster = createCluster({
|
||||
id: "some-id",
|
||||
contextName: "some-context",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
}, {
|
||||
clusterServerUrl: "foo",
|
||||
});
|
||||
|
||||
entity = new KubernetesCluster({
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
labels: {},
|
||||
},
|
||||
spec: {
|
||||
kubeconfigContext: "some-context",
|
||||
kubeconfigPath: "/some/path/to/kubeconfig",
|
||||
},
|
||||
status: {
|
||||
phase: "connecting",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("given cluster has icon, updates entity spec with icon", () => {
|
||||
cluster.preferences.icon = "some-icon";
|
||||
updateEntitySpec(entity, cluster);
|
||||
expect(entity.spec.icon?.src).toEqual("some-icon");
|
||||
});
|
||||
|
||||
it("given cluster icon is null, deletes icon from both", () => {
|
||||
cluster.preferences.icon = null;
|
||||
entity.spec.icon = { src : "some-icon" };
|
||||
|
||||
updateEntitySpec(entity, cluster);
|
||||
expect(entity.spec.icon).toBeUndefined();
|
||||
expect(cluster.preferences.icon).toBeUndefined();
|
||||
});
|
||||
|
||||
it("given entity has no metrics, adds source as local", () => {
|
||||
updateEntitySpec(entity, cluster);
|
||||
expect(entity.spec.metrics?.source).toEqual("local");
|
||||
});
|
||||
|
||||
it("given entity has metrics, does not change source", () => {
|
||||
entity.spec.metrics = { source: "some-source" };
|
||||
entity.spec.metrics.prometheus = {
|
||||
address: {
|
||||
namespace: "some-namespace",
|
||||
port: 42,
|
||||
service: "some-service",
|
||||
prefix: "some-prefix",
|
||||
},
|
||||
};
|
||||
|
||||
cluster.preferences.prometheus = {
|
||||
namespace: "some-other-namespace",
|
||||
port: 666,
|
||||
service: "some-other-service",
|
||||
prefix: "some-other-prefix",
|
||||
};
|
||||
|
||||
updateEntitySpec(entity, cluster);
|
||||
|
||||
expect(entity.spec.metrics?.source).toEqual("some-source");
|
||||
expect(entity.spec.metrics?.prometheus?.address).toEqual({
|
||||
namespace: "some-namespace",
|
||||
port: 42,
|
||||
service: "some-service",
|
||||
prefix: "some-prefix",
|
||||
});
|
||||
});
|
||||
|
||||
it("given entity has local prometheus source, updates entity spec with prometheus provider", () => {
|
||||
entity.spec.metrics = { source: "local" };
|
||||
|
||||
cluster.preferences.prometheusProvider = {
|
||||
type: "some-prometheus-provider-type",
|
||||
};
|
||||
cluster.preferences.prometheus = {
|
||||
namespace: "some-namespace",
|
||||
port: 42,
|
||||
service: "some-service",
|
||||
prefix: "some-prefix",
|
||||
};
|
||||
|
||||
updateEntitySpec(entity, cluster);
|
||||
expect(entity.spec.metrics?.prometheus?.address).toEqual({
|
||||
namespace: "some-namespace",
|
||||
port: 42,
|
||||
service: "some-service",
|
||||
prefix: "some-prefix",
|
||||
});
|
||||
|
||||
expect(entity.spec.metrics?.prometheus?.type).toEqual("some-prometheus-provider-type");
|
||||
});
|
||||
|
||||
it("given entity has no metrics, updates entity spec with prometheus provider", () => {
|
||||
expect(entity.spec.metrics).toBeUndefined();
|
||||
|
||||
cluster.preferences.prometheusProvider = {
|
||||
type: "some-prometheus-provider-type",
|
||||
};
|
||||
cluster.preferences.prometheus = {
|
||||
namespace: "some-namespace",
|
||||
port: 42,
|
||||
service: "some-service",
|
||||
prefix: "some-prefix",
|
||||
};
|
||||
|
||||
updateEntitySpec(entity, cluster);
|
||||
|
||||
expect(entity.spec.metrics?.prometheus?.address).toEqual({
|
||||
namespace: "some-namespace",
|
||||
port: 42,
|
||||
service: "some-service",
|
||||
prefix: "some-prefix",
|
||||
});
|
||||
|
||||
expect(entity.spec.metrics?.prometheus?.type).toEqual("some-prometheus-provider-type");
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user