mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Cleanup 'Cluster' to remove environment specific details (#6951)
- requestNamespaceListPermissions is infallable so no need to have the extra try/catch - Refactor isMetricHidden method away from Cluster - Refactor shouldShowResource out of Cluster - Refactor isInLocalKubeconfig out of Cluster - Remove depecrated and unused workspace from Cluster - Refactor out kubectl as a dependency of Cluster - Remove from cluster getter used only once - Split out ClusterConnection from Cluster - Also split out KubeAuthProxyServer from ContextHandler - Rename ContextHandler to PrometheusHandler - Cleanup onNetworkOffline/Online impls within ClusterManager - Remove annotations from ClusterConnection - Remove mobx annotations from Cluster - Rename loadConfigFromFileInjectable - Remove all uses of dead createClusterInjectionToken - Fix type errors related to broadcastConnectionUpdate Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
e6d6d1d8f7
commit
ad9bafe2a5
133 files changed
+2544
-1961
No files matched your search
+6
-8
@@ -3,20 +3,18 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
||||
import type { Cluster } from "../../../../common/cluster/cluster";
|
||||
import catalogEntityRegistryInjectable from "./registry.injectable";
|
||||
|
||||
export type GetActiveClusterEntity = () => Cluster | undefined;
|
||||
|
||||
const getActiveClusterEntityInjectable = getInjectable({
|
||||
id: "get-active-cluster-entity",
|
||||
instantiate: (di): GetActiveClusterEntity => {
|
||||
const activeEntityInternalClusterInjectable = getInjectable({
|
||||
id: "active-entity-internal-cluster",
|
||||
instantiate: (di) => {
|
||||
const store = di.inject(clusterStoreInjectable);
|
||||
const entityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
|
||||
return () => store.getById(entityRegistry.activeEntity?.getId());
|
||||
return computed(() => store.getById(entityRegistry.activeEntity?.getId()));
|
||||
},
|
||||
});
|
||||
|
||||
export default getActiveClusterEntityInjectable;
|
||||
export default activeEntityInternalClusterInjectable;
|
||||
@@ -5,14 +5,22 @@
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { ClusterMetricsResourceType } from "../../../../common/cluster-types";
|
||||
import getActiveClusterEntityInjectable from "./get-active-cluster-entity.injectable";
|
||||
import activeEntityInternalClusterInjectable from "./get-active-cluster-entity.injectable";
|
||||
|
||||
const enabledMetricsInjectable = getInjectable({
|
||||
id: "enabled-metrics",
|
||||
instantiate: (di, kind) => {
|
||||
const getActiveClusterEntity = di.inject(getActiveClusterEntityInjectable);
|
||||
const activeEntityInternalCluster = di.inject(activeEntityInternalClusterInjectable);
|
||||
|
||||
return computed(() => !getActiveClusterEntity()?.isMetricHidden(kind));
|
||||
return computed(() => {
|
||||
const cluster = activeEntityInternalCluster.get();
|
||||
|
||||
if (!cluster?.preferences.hiddenMetrics) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cluster.preferences.hiddenMetrics.includes(kind);
|
||||
});
|
||||
},
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, kind: ClusterMetricsResourceType) => kind,
|
||||
|
||||
+4
-4
@@ -5,8 +5,7 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
|
||||
import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable";
|
||||
import readFileInjectable from "../../../common/fs/read-file.injectable";
|
||||
import { loadConfigFromString } from "../../../common/kube-helpers";
|
||||
import loadKubeconfigInjectable from "../../../common/cluster/load-kubeconfig.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import openDeleteClusterDialogInjectable from "../../components/delete-cluster-dialog/open.injectable";
|
||||
import { beforeFrameStartsSecondInjectionToken } from "../tokens";
|
||||
@@ -18,7 +17,6 @@ const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
|
||||
run: () => {
|
||||
const catalogCategoryRegistry = di.inject(catalogCategoryRegistryInjectable);
|
||||
const openDeleteClusterDialog = di.inject(openDeleteClusterDialogInjectable);
|
||||
const readFile = di.inject(readFileInjectable);
|
||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
@@ -37,7 +35,9 @@ const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
|
||||
return logger.warn("[KUBERNETES-CLUSTER]: cannot delete cluster, does not exist in store", { clusterId });
|
||||
}
|
||||
|
||||
const result = loadConfigFromString(await readFile(cluster.kubeConfigPath));
|
||||
const loadKubeconfig = di.inject(loadKubeconfigInjectable, cluster);
|
||||
|
||||
const result = await loadKubeconfig(true);
|
||||
|
||||
if (result.error) {
|
||||
logger.error("[KUBERNETES-CLUSTER]: failed to parse kubeconfig file", result.error);
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
|
||||
&& cluster.accessibleNamespaces.length === 0
|
||||
&& allNamespaces.get().every(ns => namespaces.includes(ns))
|
||||
),
|
||||
isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled,
|
||||
isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled.get(),
|
||||
get allNamespaces() {
|
||||
return allNamespaces.get();
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ const shouldShowResourceInjectable = getInjectable({
|
||||
const cluster = di.inject(hostedClusterInjectable);
|
||||
|
||||
return cluster
|
||||
? computed(() => cluster.shouldShowResource(resource))
|
||||
? computed(() => cluster.resourcesToShow.has(formatKubeApiResource(resource)))
|
||||
: computed(() => false);
|
||||
},
|
||||
injectionToken: shouldShowResourceInjectionToken,
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* 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 { ClusterDependencies } from "../../common/cluster/cluster";
|
||||
import { Cluster } from "../../common/cluster/cluster";
|
||||
import directoryForKubeConfigsInjectable from "../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
|
||||
import loadConfigfromFileInjectable from "../../common/kube-helpers/load-config-from-file.injectable";
|
||||
|
||||
const createClusterInjectable = getInjectable({
|
||||
id: "create-cluster",
|
||||
|
||||
instantiate: (di) => {
|
||||
const dependencies: ClusterDependencies = {
|
||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
broadcastMessage: di.inject(broadcastMessageInjectable),
|
||||
loadConfigfromFile: di.inject(loadConfigfromFileInjectable),
|
||||
|
||||
// TODO: Dismantle wrong abstraction
|
||||
// Note: "as never" to get around strictness in unnatural scenario
|
||||
createKubeconfigManager: () => undefined as never,
|
||||
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
|
||||
createContextHandler: () => undefined as never,
|
||||
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
requestNamespaceListPermissionsFor: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
requestApiResources: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
detectClusterMetadata: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
clusterVersionDetector: {
|
||||
detect: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
key: "irrelavent",
|
||||
},
|
||||
};
|
||||
|
||||
return (model, configData) => new Cluster(dependencies, model, configData);
|
||||
},
|
||||
|
||||
injectionToken: createClusterInjectionToken,
|
||||
});
|
||||
|
||||
export default createClusterInjectable;
|
||||
@@ -6,6 +6,7 @@
|
||||
import styles from "./cluster-overview.module.scss";
|
||||
|
||||
import React from "react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import type { NodeStore } from "../+nodes/store";
|
||||
@@ -22,32 +23,30 @@ import type { EventStore } from "../+events/store";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
||||
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import assert from "assert";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import podStoreInjectable from "../+workloads-pods/store.injectable";
|
||||
import eventStoreInjectable from "../+events/store.injectable";
|
||||
import nodeStoreInjectable from "../+nodes/store.injectable";
|
||||
import enabledMetricsInjectable from "../../api/catalog/entity/metrics-enabled.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
subscribeStores: SubscribeStores;
|
||||
clusterOverviewStore: ClusterOverviewStore;
|
||||
hostedCluster: Cluster;
|
||||
podStore: PodStore;
|
||||
eventStore: EventStore;
|
||||
nodeStore: NodeStore;
|
||||
clusterMetricsAreVisible: IComputedValue<boolean>;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||
private metricPoller = interval(60, () => this.loadMetrics());
|
||||
|
||||
loadMetrics() {
|
||||
if (this.props.hostedCluster.available) {
|
||||
this.props.clusterOverviewStore.loadMetrics();
|
||||
private readonly metricPoller = interval(60, async () => {
|
||||
try {
|
||||
await this.props.clusterOverviewStore.loadMetrics();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
this.metricPoller.start(true);
|
||||
@@ -97,14 +96,13 @@ class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { eventStore, nodeStore, hostedCluster } = this.props;
|
||||
const { eventStore, nodeStore, clusterMetricsAreVisible } = this.props;
|
||||
const isLoaded = nodeStore.isLoaded && eventStore.isLoaded;
|
||||
const isMetricHidden = hostedCluster.isMetricHidden(ClusterMetricsResourceType.Cluster);
|
||||
|
||||
return (
|
||||
<TabLayout scrollable>
|
||||
<div className={styles.ClusterOverview} data-testid="cluster-overview-page">
|
||||
{this.renderClusterOverview(isLoaded, isMetricHidden)}
|
||||
{this.renderClusterOverview(isLoaded, clusterMetricsAreVisible.get())}
|
||||
</div>
|
||||
</TabLayout>
|
||||
);
|
||||
@@ -112,18 +110,12 @@ class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||
}
|
||||
|
||||
export const ClusterOverview = withInjectables<Dependencies>(NonInjectedClusterOverview, {
|
||||
getProps: (di) => {
|
||||
const hostedCluster = di.inject(hostedClusterInjectable);
|
||||
|
||||
assert(hostedCluster, "Only allowed to renderer ClusterOverview within cluster frame");
|
||||
|
||||
return {
|
||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
||||
hostedCluster,
|
||||
podStore: di.inject(podStoreInjectable),
|
||||
eventStore: di.inject(eventStoreInjectable),
|
||||
nodeStore: di.inject(nodeStoreInjectable),
|
||||
};
|
||||
},
|
||||
getProps: (di) => ({
|
||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
||||
clusterMetricsAreVisible: di.inject(enabledMetricsInjectable, ClusterMetricsResourceType.Cluster),
|
||||
podStore: di.inject(podStoreInjectable),
|
||||
eventStore: di.inject(eventStoreInjectable),
|
||||
nodeStore: di.inject(nodeStoreInjectable),
|
||||
}),
|
||||
});
|
||||
+2
-4
@@ -11,8 +11,8 @@ import { renderFor } from "../../test-utils/renderFor";
|
||||
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
|
||||
import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import hostedClusterInjectable from "../../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../../cluster/create-cluster.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import { Cluster } from "../../../../common/cluster/cluster";
|
||||
|
||||
jest.mock("../../kube-object-meta/kube-object-meta", () => ({
|
||||
KubeObjectMeta: () => null,
|
||||
@@ -27,9 +27,7 @@ describe("SecretDetails tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -11,12 +11,12 @@ import { fireEvent } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { Fetch } from "../../../common/fetch/fetch.injectable";
|
||||
import fetchInjectable from "../../../common/fetch/fetch.injectable";
|
||||
import { Namespace } from "../../../common/k8s-api/endpoints";
|
||||
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||
@@ -58,9 +58,7 @@ describe("<NamespaceSelectFilter />", () => {
|
||||
fetchMock = asyncFn();
|
||||
di.override(fetchInjectable, () => fetchMock);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -7,9 +7,9 @@ import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
import { Namespace } from "../../../common/k8s-api/endpoints";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||
import type { NamespaceStore } from "./store";
|
||||
@@ -111,9 +111,7 @@ describe("NamespaceStore", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
+2
-4
@@ -15,11 +15,11 @@ import storesAndApisCanBeCreatedInjectable from "../../../../stores-apis-can-be-
|
||||
import directoryForUserDataInjectable from "../../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../../../cluster/create-cluster.injectable";
|
||||
import type { CloseClusterRoleBindingDialog } from "../dialog/close.injectable";
|
||||
import closeClusterRoleBindingDialogInjectable from "../dialog/close.injectable";
|
||||
import type { OpenClusterRoleBindingDialog } from "../dialog/open.injectable";
|
||||
import openClusterRoleBindingDialogInjectable from "../dialog/open.injectable";
|
||||
import { Cluster } from "../../../../../common/cluster/cluster";
|
||||
|
||||
describe("ClusterRoleBindingDialog tests", () => {
|
||||
let render: DiRender;
|
||||
@@ -36,9 +36,7 @@ describe("ClusterRoleBindingDialog tests", () => {
|
||||
closeClusterRoleBindingDialog = di.inject(closeClusterRoleBindingDialogInjectable);
|
||||
openClusterRoleBindingDialog = di.inject(openClusterRoleBindingDialogInjectable);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
+2
-4
@@ -15,9 +15,9 @@ import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable";
|
||||
import storesAndApisCanBeCreatedInjectable from "../../../../stores-apis-can-be-created.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../../../cluster/create-cluster.injectable";
|
||||
import type { OpenRoleBindingDialog } from "../dialog/open.injectable";
|
||||
import openRoleBindingDialogInjectable from "../dialog/open.injectable";
|
||||
import { Cluster } from "../../../../../common/cluster/cluster";
|
||||
|
||||
describe("RoleBindingDialog tests", () => {
|
||||
let render: DiRender;
|
||||
@@ -32,9 +32,7 @@ describe("RoleBindingDialog tests", () => {
|
||||
|
||||
openRoleBindingDialog = di.inject(openRoleBindingDialogInjectable);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -23,8 +23,6 @@ import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import daemonSetStoreInjectable from "./store.injectable";
|
||||
import podStoreInjectable from "../+workloads-pods/store.injectable";
|
||||
import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable";
|
||||
import requestPodMetricsForDaemonSetsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-pod-metrics-for-daemon-sets.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
|
||||
export interface DaemonSetDetailsProps extends KubeObjectDetailsProps<DaemonSet> {
|
||||
@@ -109,8 +107,6 @@ export const DaemonSetDetails = withInjectables<Dependencies, DaemonSetDetailsPr
|
||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||
daemonSetStore: di.inject(daemonSetStoreInjectable),
|
||||
podStore: di.inject(podStoreInjectable),
|
||||
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
|
||||
requestPodMetricsForDaemonSets: di.inject(requestPodMetricsForDaemonSetsInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -22,8 +22,8 @@ import type { PortForwardStore } from "../../port-forward";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable";
|
||||
import type { GetActiveClusterEntity } from "../../api/catalog/entity/get-active-cluster-entity.injectable";
|
||||
import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import enabledMetricsInjectable from "../../api/catalog/entity/metrics-enabled.injectable";
|
||||
|
||||
export interface PodDetailsContainerProps {
|
||||
pod: Pod;
|
||||
@@ -33,7 +33,7 @@ export interface PodDetailsContainerProps {
|
||||
|
||||
interface Dependencies {
|
||||
portForwardStore: PortForwardStore;
|
||||
getActiveClusterEntity: GetActiveClusterEntity;
|
||||
containerMetricsVisible: IComputedValue<boolean>;
|
||||
}
|
||||
|
||||
@observer
|
||||
@@ -84,7 +84,7 @@ class NonInjectedPodDetailsContainer extends React.Component<PodDetailsContainer
|
||||
}
|
||||
|
||||
render() {
|
||||
const { pod, container, metrics, getActiveClusterEntity } = this.props;
|
||||
const { pod, container, metrics, containerMetricsVisible } = this.props;
|
||||
|
||||
if (!pod || !container) return null;
|
||||
const { name, image, imagePullPolicy, ports, volumeMounts, command, args } = container;
|
||||
@@ -97,7 +97,7 @@ class NonInjectedPodDetailsContainer extends React.Component<PodDetailsContainer
|
||||
const readiness = pod.getReadinessProbe(container);
|
||||
const startup = pod.getStartupProbe(container);
|
||||
const isInitContainer = !!pod.getInitContainers().find(c => c.name == name);
|
||||
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Container);
|
||||
const isMetricHidden = containerMetricsVisible.get();
|
||||
|
||||
return (
|
||||
<div className="PodDetailsContainer">
|
||||
@@ -217,6 +217,6 @@ export const PodDetailsContainer = withInjectables<Dependencies, PodDetailsConta
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
portForwardStore: di.inject(portForwardStoreInjectable),
|
||||
getActiveClusterEntity: di.inject(getActiveClusterEntityInjectable),
|
||||
containerMetricsVisible: di.inject(enabledMetricsInjectable, ClusterMetricsResourceType.Container),
|
||||
}),
|
||||
});
|
||||
@@ -10,7 +10,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const scheduledCronJob = new CronJob({
|
||||
apiVersion: "foo",
|
||||
@@ -127,9 +127,7 @@ describe("CronJob Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const runningDaemonSet = new DaemonSet({
|
||||
apiVersion: "foo",
|
||||
@@ -144,9 +144,7 @@ describe("DaemonSet Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -14,7 +14,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const spec: PodSpec = {
|
||||
containers: [{
|
||||
@@ -216,9 +216,7 @@ describe("Deployment Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const runningJob = new Job({
|
||||
apiVersion: "foo",
|
||||
@@ -181,9 +181,7 @@ describe("Job Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const runningPod = new Pod({
|
||||
apiVersion: "foo",
|
||||
@@ -127,9 +127,7 @@ describe("Pod Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const runningReplicaSet = new ReplicaSet({
|
||||
apiVersion: "foo",
|
||||
@@ -144,9 +144,7 @@ describe("ReplicaSet Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
const runningStatefulSet = new StatefulSet({
|
||||
apiVersion: "foo",
|
||||
@@ -144,9 +144,7 @@ describe("StatefulSet Store tests", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -56,7 +56,7 @@ export class ClusterFrameHandler {
|
||||
const iframe = document.createElement("iframe");
|
||||
|
||||
iframe.id = `cluster-frame-${cluster.id}`;
|
||||
iframe.name = cluster.contextName;
|
||||
iframe.name = cluster.contextName.get();
|
||||
iframe.setAttribute("src", getClusterFrameUrl(clusterId));
|
||||
iframe.addEventListener("load", action(() => {
|
||||
this.dependencies.logger.info(`[LENS-VIEW]: frame for clusterId=${clusterId} has loaded`);
|
||||
@@ -71,19 +71,19 @@ export class ClusterFrameHandler {
|
||||
this.dependencies.logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`);
|
||||
|
||||
const dispose = when(
|
||||
() => cluster.ready,
|
||||
() => cluster.ready.get(),
|
||||
() => this.dependencies.logger.info(`[LENS-VIEW]: cluster is ready, clusterId=${clusterId}`),
|
||||
);
|
||||
|
||||
when(
|
||||
// cluster.disconnect is set to `false` when the cluster starts to connect
|
||||
() => !cluster.disconnected,
|
||||
() => !cluster.disconnected.get(),
|
||||
() => {
|
||||
when(
|
||||
() => {
|
||||
const cluster = this.dependencies.getClusterById(clusterId);
|
||||
|
||||
return Boolean(!cluster || (cluster.disconnected && this.views.get(clusterId)?.isLoaded));
|
||||
return Boolean(!cluster || (cluster.disconnected.get() && this.views.get(clusterId)?.isLoaded));
|
||||
},
|
||||
() => {
|
||||
this.dependencies.logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`);
|
||||
@@ -126,7 +126,7 @@ export class ClusterFrameHandler {
|
||||
() => {
|
||||
const view = this.views.get(clusterId);
|
||||
|
||||
if (cluster.available && cluster.ready && view?.isLoaded) {
|
||||
if (cluster.available.get() && cluster.ready.get() && view?.isLoaded) {
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ class NonInjectedClusterStatus extends React.Component<ClusterStatusProps & Depe
|
||||
return (
|
||||
<div className={cssNames(styles.status, "flex column box center align-center justify-center", this.props.className)}>
|
||||
<div className="flex items-center column gaps">
|
||||
<h2>{this.entity?.getName() ?? this.cluster.name}</h2>
|
||||
<h2>{this.entity?.getName() ?? this.cluster.name.get()}</h2>
|
||||
{this.renderStatusIcon()}
|
||||
{this.renderAuthenticationOutput()}
|
||||
{this.renderReconnectionHelp()}
|
||||
|
||||
@@ -53,7 +53,11 @@ class NonInjectedClusterView extends React.Component<Dependencies> {
|
||||
@computed get isReady(): boolean {
|
||||
const { cluster } = this;
|
||||
|
||||
return (cluster?.ready && cluster?.available && this.isViewLoaded.get()) ?? false;
|
||||
if (!cluster) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cluster.ready.get() && cluster.available.get() && this.isViewLoaded.get();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
+62
-36
@@ -8,17 +8,19 @@ import { waitFor } from "@testing-library/react";
|
||||
import { ClusterLocalTerminalSetting } from "../local-terminal-settings";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import type { Stats } from "fs";
|
||||
import type { Cluster } from "../../../../common/cluster/cluster";
|
||||
import { Cluster } from "../../../../common/cluster/cluster";
|
||||
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
||||
import type { DiRender } from "../../test-utils/renderFor";
|
||||
import { renderFor } from "../../test-utils/renderFor";
|
||||
import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable";
|
||||
import statInjectable from "../../../../common/fs/stat.injectable";
|
||||
import loadKubeconfigInjectable from "../../../../common/cluster/load-kubeconfig.injectable";
|
||||
|
||||
describe("ClusterLocalTerminalSettings", () => {
|
||||
let render: DiRender;
|
||||
let showErrorNotificationMock: jest.Mock;
|
||||
let statMock: jest.Mock;
|
||||
let loadKubeconfigMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting();
|
||||
@@ -34,27 +36,28 @@ describe("ClusterLocalTerminalSettings", () => {
|
||||
() => showErrorNotificationMock,
|
||||
);
|
||||
|
||||
loadKubeconfigMock = jest.fn();
|
||||
di.override(loadKubeconfigInjectable, () => loadKubeconfigMock);
|
||||
|
||||
render = renderFor(di);
|
||||
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should render without errors", () => {
|
||||
const dom = render(<ClusterLocalTerminalSetting cluster={null as never}/>);
|
||||
|
||||
expect(dom.container).toBeInstanceOf(HTMLElement);
|
||||
});
|
||||
|
||||
it("should render the current settings", async () => {
|
||||
const cluster = {
|
||||
loadKubeconfigMock.mockImplementation(() => ({
|
||||
getContextObject: () => ({}),
|
||||
}));
|
||||
|
||||
const cluster = new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some/path",
|
||||
preferences: {
|
||||
terminalCWD: "/foobar",
|
||||
defaultNamespace: "kube-system",
|
||||
},
|
||||
getKubeconfig: jest.fn(() => ({
|
||||
getContextObject: jest.fn(() => ({})),
|
||||
})),
|
||||
} as unknown as Cluster;
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:12345",
|
||||
});
|
||||
const dom = render(<ClusterLocalTerminalSetting cluster={cluster}/>);
|
||||
|
||||
expect(await dom.findByDisplayValue("/foobar")).toBeDefined();
|
||||
@@ -62,16 +65,21 @@ describe("ClusterLocalTerminalSettings", () => {
|
||||
});
|
||||
|
||||
it("should change placeholder for 'Default Namespace' to be the namespace from the kubeconfig", async () => {
|
||||
const cluster = {
|
||||
loadKubeconfigMock.mockImplementation(() => ({
|
||||
getContextObject: () => ({ namespace: "blat" }),
|
||||
}));
|
||||
|
||||
const cluster = new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some/path",
|
||||
preferences: {
|
||||
terminalCWD: "/foobar",
|
||||
},
|
||||
getKubeconfig: jest.fn(() => ({
|
||||
getContextObject: jest.fn(() => ({
|
||||
namespace: "blat",
|
||||
})),
|
||||
})),
|
||||
} as unknown as Cluster;
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:12345",
|
||||
});
|
||||
|
||||
const dom = render(<ClusterLocalTerminalSetting cluster={cluster}/>);
|
||||
|
||||
expect(await dom.findByDisplayValue("/foobar")).toBeDefined();
|
||||
@@ -79,14 +87,20 @@ describe("ClusterLocalTerminalSettings", () => {
|
||||
});
|
||||
|
||||
it("should save the new default namespace after clicking away", async () => {
|
||||
const cluster = {
|
||||
loadKubeconfigMock.mockImplementation(() => ({
|
||||
getContextObject: () => ({}),
|
||||
}));
|
||||
|
||||
const cluster = new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some/path",
|
||||
preferences: {
|
||||
terminalCWD: "/foobar",
|
||||
},
|
||||
getKubeconfig: jest.fn(() => ({
|
||||
getContextObject: jest.fn(() => ({})),
|
||||
})),
|
||||
} as unknown as Cluster;
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:12345",
|
||||
});
|
||||
|
||||
const dom = render(<ClusterLocalTerminalSetting cluster={cluster}/>);
|
||||
const dn = await dom.findByTestId("default-namespace");
|
||||
@@ -107,11 +121,17 @@ describe("ClusterLocalTerminalSettings", () => {
|
||||
} as Stats;
|
||||
});
|
||||
|
||||
const cluster = {
|
||||
getKubeconfig: jest.fn(() => ({
|
||||
getContextObject: jest.fn(() => ({})),
|
||||
})),
|
||||
} as unknown as Cluster;
|
||||
loadKubeconfigMock.mockImplementation(() => ({
|
||||
getContextObject: () => ({}),
|
||||
}));
|
||||
|
||||
const cluster = new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some/path",
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:12345",
|
||||
});
|
||||
|
||||
const dom = render(<ClusterLocalTerminalSetting cluster={cluster}/>);
|
||||
const dn = await dom.findByTestId("working-directory");
|
||||
@@ -133,11 +153,17 @@ describe("ClusterLocalTerminalSettings", () => {
|
||||
} as Stats;
|
||||
});
|
||||
|
||||
const cluster = {
|
||||
getKubeconfig: jest.fn(() => ({
|
||||
getContextObject: jest.fn(() => ({})),
|
||||
})),
|
||||
} as unknown as Cluster;
|
||||
loadKubeconfigMock.mockImplementation(() => ({
|
||||
getContextObject: () => ({}),
|
||||
}));
|
||||
|
||||
const cluster = new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some/path",
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:12345",
|
||||
});
|
||||
|
||||
const dom = render(<ClusterLocalTerminalSetting cluster={cluster}/>);
|
||||
const dn = await dom.findByTestId("working-directory");
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ClusterKubeconfig extends React.Component<ClusterKubeconfigProps> {
|
||||
openKubeconfig = () => {
|
||||
const { cluster } = this.props;
|
||||
|
||||
shell.showItemInFolder(cluster.kubeConfigPath);
|
||||
shell.showItemInFolder(cluster.kubeConfigPath.get());
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -27,7 +27,9 @@ export class ClusterKubeconfig extends React.Component<ClusterKubeconfigProps> {
|
||||
<Notice className="mb-14 mt-3">
|
||||
<SubTitle title="Kubeconfig" />
|
||||
<span>
|
||||
<a className="link value" onClick={this.openKubeconfig}>{this.props.cluster.kubeConfigPath}</a>
|
||||
<a className="link value" onClick={this.openKubeconfig}>
|
||||
{this.props.cluster.kubeConfigPath.get()}
|
||||
</a>
|
||||
</span>
|
||||
</Notice>
|
||||
);
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import loadKubeconfigInjectable from "../../../common/cluster/load-kubeconfig.injectable";
|
||||
|
||||
export interface LocalTerminalSettingPresenter {
|
||||
readonly directory: {
|
||||
get: () => string;
|
||||
set: (value: string) => void;
|
||||
};
|
||||
readonly defaultNamespace: {
|
||||
get: () => string;
|
||||
set: (value: string) => void;
|
||||
};
|
||||
readonly placeholderDefaultNamespace: string;
|
||||
}
|
||||
|
||||
const localTerminalSettingPresenterInjectable = getInjectable({
|
||||
id: "local-terminal-setting-presenter",
|
||||
instantiate: async (di, cluster: Cluster): Promise<LocalTerminalSettingPresenter> => {
|
||||
const loadKubeconfig = di.inject(loadKubeconfigInjectable, cluster);
|
||||
|
||||
const kubeconfig = await loadKubeconfig();
|
||||
|
||||
const directory = observable.box(cluster.preferences.terminalCWD || "");
|
||||
const defaultNamespace = observable.box(cluster.preferences.defaultNamespace || "");
|
||||
const placeholderDefaultNamespace = kubeconfig.getContextObject(cluster.contextName.get())?.namespace || "default";
|
||||
|
||||
return {
|
||||
directory: {
|
||||
get: () => directory.get(),
|
||||
set: (value) => directory.set(value),
|
||||
},
|
||||
defaultNamespace: {
|
||||
get: () => defaultNamespace.get(),
|
||||
set: (value) => defaultNamespace.set(value),
|
||||
},
|
||||
placeholderDefaultNamespace,
|
||||
};
|
||||
},
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
});
|
||||
|
||||
export default localTerminalSettingPresenterInjectable;
|
||||
+56
-63
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { Input } from "../input";
|
||||
@@ -20,6 +20,10 @@ import Gutter from "../gutter/gutter";
|
||||
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||
import type { OpenPathPickingDialog } from "../../../features/path-picking-dialog/renderer/pick-paths.injectable";
|
||||
import openPathPickingDialogInjectable from "../../../features/path-picking-dialog/renderer/pick-paths.injectable";
|
||||
import type { LocalTerminalSettingPresenter } from "./local-terminal-setting-presenter.injectable";
|
||||
import localTerminalSettingPresenterInjectable from "./local-terminal-setting-presenter.injectable";
|
||||
import { Spinner } from "../spinner";
|
||||
import { action, runInAction } from "mobx";
|
||||
|
||||
export interface ClusterLocalTerminalSettingProps {
|
||||
cluster: Cluster;
|
||||
@@ -30,70 +34,57 @@ interface Dependencies {
|
||||
resolveTilde: ResolveTilde;
|
||||
openPathPickingDialog: OpenPathPickingDialog;
|
||||
isWindows: boolean;
|
||||
presenter: LocalTerminalSettingPresenter;
|
||||
}
|
||||
|
||||
const NonInjectedClusterLocalTerminalSetting = observer(({
|
||||
cluster,
|
||||
showErrorNotification,
|
||||
validateDirectory,
|
||||
resolveTilde,
|
||||
isWindows,
|
||||
openPathPickingDialog,
|
||||
}: Dependencies & ClusterLocalTerminalSettingProps) => {
|
||||
if (!cluster) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [directory, setDirectory] = useState<string>(cluster.preferences?.terminalCWD || "");
|
||||
const [defaultNamespace, setDefaultNamespaces] = useState<string>(cluster.preferences?.defaultNamespace || "");
|
||||
const [placeholderDefaultNamespace, setPlaceholderDefaultNamespace] = useState("default");
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const kubeconfig = await cluster.getKubeconfig();
|
||||
const { namespace } = kubeconfig.getContextObject(cluster.contextName) ?? {};
|
||||
|
||||
if (namespace) {
|
||||
setPlaceholderDefaultNamespace(namespace);
|
||||
}
|
||||
})();
|
||||
setDirectory(cluster.preferences?.terminalCWD || "");
|
||||
setDefaultNamespaces(cluster.preferences?.defaultNamespace || "");
|
||||
}, [cluster]);
|
||||
|
||||
const NonInjectedClusterLocalTerminalSetting = observer((props: Dependencies & ClusterLocalTerminalSettingProps) => {
|
||||
const {
|
||||
cluster,
|
||||
showErrorNotification,
|
||||
validateDirectory,
|
||||
resolveTilde,
|
||||
isWindows,
|
||||
openPathPickingDialog,
|
||||
presenter,
|
||||
} = props;
|
||||
const commitDirectory = async (directory: string) => {
|
||||
cluster.preferences ??= {};
|
||||
|
||||
if (!directory) {
|
||||
cluster.preferences.terminalCWD = undefined;
|
||||
} else {
|
||||
const dir = resolveTilde(directory);
|
||||
const result = await validateDirectory(dir);
|
||||
runInAction(() => {
|
||||
cluster.preferences.terminalCWD = undefined;
|
||||
});
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
showErrorNotification(
|
||||
<>
|
||||
<b>Terminal Working Directory</b>
|
||||
<p>
|
||||
{"Your changes were not saved because "}
|
||||
{result.error}
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
} else {
|
||||
cluster.preferences.terminalCWD = dir;
|
||||
setDirectory(dir);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const dir = resolveTilde(directory);
|
||||
const result = await validateDirectory(dir);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
runInAction(() => {
|
||||
cluster.preferences.terminalCWD = dir;
|
||||
presenter.directory.set(dir);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
showErrorNotification(
|
||||
<>
|
||||
<b>Terminal Working Directory</b>
|
||||
<p>
|
||||
{"Your changes were not saved because "}
|
||||
{result.error}
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
};
|
||||
|
||||
const commitDefaultNamespace = () => {
|
||||
cluster.preferences ??= {};
|
||||
cluster.preferences.defaultNamespace = defaultNamespace || undefined;
|
||||
};
|
||||
const commitDefaultNamespace = action(() => {
|
||||
cluster.preferences.defaultNamespace = presenter.defaultNamespace.get() || undefined;
|
||||
});
|
||||
|
||||
const setAndCommitDirectory = (newPath: string) => {
|
||||
setDirectory(newPath);
|
||||
presenter.directory.set(newPath);
|
||||
commitDirectory(newPath);
|
||||
};
|
||||
|
||||
@@ -112,15 +103,15 @@ const NonInjectedClusterLocalTerminalSetting = observer(({
|
||||
<SubTitle title="Working Directory"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
value={directory}
|
||||
value={presenter.directory.get()}
|
||||
data-testid="working-directory"
|
||||
onChange={setDirectory}
|
||||
onBlur={() => commitDirectory(directory)}
|
||||
onChange={value => presenter.directory.set(value)}
|
||||
onBlur={() => commitDirectory(presenter.directory.get())}
|
||||
placeholder={isWindows ? "$USERPROFILE" : "$HOME"}
|
||||
iconRight={(
|
||||
<>
|
||||
{
|
||||
directory && (
|
||||
presenter.directory.get() && (
|
||||
<Icon
|
||||
material="close"
|
||||
title="Clear"
|
||||
@@ -151,10 +142,10 @@ const NonInjectedClusterLocalTerminalSetting = observer(({
|
||||
<Input
|
||||
theme="round-black"
|
||||
data-testid="default-namespace"
|
||||
value={defaultNamespace}
|
||||
onChange={setDefaultNamespaces}
|
||||
value={presenter.defaultNamespace.get()}
|
||||
onChange={value => presenter.defaultNamespace.set(value)}
|
||||
onBlur={commitDefaultNamespace}
|
||||
placeholder={placeholderDefaultNamespace}
|
||||
placeholder={presenter.placeholderDefaultNamespace}
|
||||
/>
|
||||
<small className="hint">
|
||||
Default namespace used for kubectl.
|
||||
@@ -165,12 +156,14 @@ const NonInjectedClusterLocalTerminalSetting = observer(({
|
||||
});
|
||||
|
||||
export const ClusterLocalTerminalSetting = withInjectables<Dependencies, ClusterLocalTerminalSettingProps>(NonInjectedClusterLocalTerminalSetting, {
|
||||
getProps: (di, props) => ({
|
||||
getPlaceholder: () => <Spinner center />,
|
||||
getProps: async (di, props) => ({
|
||||
...props,
|
||||
showErrorNotification: di.inject(showErrorNotificationInjectable),
|
||||
validateDirectory: di.inject(validateDirectoryInjectable),
|
||||
resolveTilde: di.inject(resolveTildeInjectable),
|
||||
isWindows: di.inject(isWindowsInjectable),
|
||||
openPathPickingDialog: di.inject(openPathPickingDialogInjectable),
|
||||
presenter: await di.inject(localTerminalSettingPresenterInjectable, props.cluster),
|
||||
}),
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { makeObservable, observable } from "mobx";
|
||||
import { makeObservable, observable, runInAction } from "mobx";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import React from "react";
|
||||
import { Input } from "../input/input";
|
||||
@@ -28,9 +28,10 @@ export class ClusterNodeShellSetting extends React.Component<ClusterNodeShellSet
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.cluster.preferences ??= {};
|
||||
this.props.cluster.preferences.nodeShellImage = this.nodeShellImage || undefined;
|
||||
this.props.cluster.preferences.imagePullSecret = this.imagePullSecret || undefined;
|
||||
runInAction(() => {
|
||||
this.props.cluster.preferences.nodeShellImage = this.nodeShellImage || undefined;
|
||||
this.props.cluster.preferences.imagePullSecret = this.imagePullSecret || undefined;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -6,5 +6,5 @@ import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
export function isCurrentContext(config: KubeConfig, cluster: Cluster) {
|
||||
return config.currentContext == cluster.contextName;
|
||||
return config.currentContext == cluster.contextName.get();
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
export type IsInLocalKubeconfig = (cluster: Cluster) => boolean;
|
||||
|
||||
const isInLocalKubeconfigInjectable = getInjectable({
|
||||
id: "is-in-local-kubeconfig",
|
||||
instantiate: (di): IsInLocalKubeconfig => {
|
||||
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
|
||||
|
||||
return cluster => cluster.kubeConfigPath.get().startsWith(directoryForKubeConfigs);
|
||||
},
|
||||
});
|
||||
|
||||
export default isInLocalKubeconfigInjectable;
|
||||
@@ -30,6 +30,8 @@ import type { SaveKubeconfig } from "./save-kubeconfig.injectable";
|
||||
import saveKubeconfigInjectable from "./save-kubeconfig.injectable";
|
||||
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
|
||||
import { isCurrentContext } from "./is-current-context";
|
||||
import type { IsInLocalKubeconfig } from "./is-in-local-kubeconfig.injectable";
|
||||
import isInLocalKubeconfigInjectable from "./is-in-local-kubeconfig.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
state: IObservableValue<DeleteClusterDialogState | undefined>;
|
||||
@@ -39,6 +41,7 @@ interface Dependencies {
|
||||
requestClearClusterAsDeleting: RequestClearClusterAsDeleting;
|
||||
showErrorNotification: ShowNotification;
|
||||
saveKubeconfig: SaveKubeconfig;
|
||||
isInLocalKubeconfig: IsInLocalKubeconfig;
|
||||
}
|
||||
|
||||
@observer
|
||||
@@ -52,7 +55,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
this.props.state.set({
|
||||
...state,
|
||||
config: Object.assign(config, {
|
||||
contexts: config.contexts.filter(item => item.name !== state.cluster.contextName),
|
||||
contexts: config.contexts.filter(item => item.name !== state.cluster.contextName.get()),
|
||||
currentContext: newCurrentContext && showContextSwitch
|
||||
? newCurrentContext
|
||||
: config.currentContext,
|
||||
@@ -61,7 +64,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
});
|
||||
|
||||
try {
|
||||
await this.props.saveKubeconfig(config, cluster.kubeConfigPath);
|
||||
await this.props.saveKubeconfig(config, cluster.kubeConfigPath.get());
|
||||
this.props.hotbarStore.removeAllHotbarItems(cluster.id);
|
||||
await this.props.requestDeleteCluster(cluster.id);
|
||||
} catch(error) {
|
||||
@@ -73,7 +76,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
}
|
||||
|
||||
shouldDeleteBeDisabled({ cluster, config, newCurrentContext, showContextSwitch }: DeleteClusterDialogState): boolean {
|
||||
const noContextsAvailable = config.contexts.filter(context => context.name !== cluster.contextName).length == 0;
|
||||
const noContextsAvailable = config.contexts.filter(context => context.name !== cluster.contextName.get()).length == 0;
|
||||
const newContextNotSelected = newCurrentContext === "";
|
||||
|
||||
if (noContextsAvailable) {
|
||||
@@ -92,7 +95,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
|
||||
const selectOptions = config
|
||||
.contexts
|
||||
.filter(context => context.name !== cluster.contextName)
|
||||
.filter(context => context.name !== cluster.contextName.get())
|
||||
.map(context => ({
|
||||
value: context.name,
|
||||
label: context.name,
|
||||
@@ -121,7 +124,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
}
|
||||
|
||||
renderDeleteMessage({ cluster }: DeleteClusterDialogState) {
|
||||
if (cluster.isInLocalKubeconfig()) {
|
||||
if (this.props.isInLocalKubeconfig(cluster)) {
|
||||
return (
|
||||
<div>
|
||||
{"Delete the "}
|
||||
@@ -141,7 +144,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
</b>
|
||||
{" context from "}
|
||||
<b>
|
||||
{cluster.kubeConfigPath}
|
||||
{cluster.kubeConfigPath.get()}
|
||||
</b>
|
||||
?
|
||||
</div>
|
||||
@@ -149,7 +152,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
}
|
||||
|
||||
getWarningMessage({ cluster, config }: DeleteClusterDialogState) {
|
||||
if (cluster.isInLocalKubeconfig()) {
|
||||
if (this.props.isInLocalKubeconfig(cluster)) {
|
||||
return (
|
||||
<p data-testid="internal-kubeconfig-warning">
|
||||
Are you sure you want to delete it? It can be re-added through the copy/paste mechanism.
|
||||
@@ -157,7 +160,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
);
|
||||
}
|
||||
|
||||
const contexts = config.contexts.filter(context => context.name !== cluster.contextName);
|
||||
const contexts = config.contexts.filter(context => context.name !== cluster.contextName.get());
|
||||
|
||||
if (!contexts.length) {
|
||||
return (
|
||||
@@ -191,7 +194,7 @@ class NonInjectedDeleteClusterDialog extends React.Component<Dependencies> {
|
||||
|
||||
renderContents(state: DeleteClusterDialogState) {
|
||||
const { config, cluster, showContextSwitch } = state;
|
||||
const contexts = state.config.contexts.filter(context => context.name !== state.cluster.contextName);
|
||||
const contexts = state.config.contexts.filter(context => context.name !== state.cluster.contextName.get());
|
||||
const disableDelete = this.shouldDeleteBeDisabled(state);
|
||||
const currentContext = isCurrentContext(config, cluster);
|
||||
|
||||
@@ -271,5 +274,6 @@ export const DeleteClusterDialog = withInjectables<Dependencies>(NonInjectedDele
|
||||
requestDeleteCluster: di.inject(requestDeleteClusterInjectable),
|
||||
saveKubeconfig: di.inject(saveKubeconfigInjectable),
|
||||
showErrorNotification: di.inject(showErrorNotificationInjectable),
|
||||
isInLocalKubeconfig: di.inject(isInLocalKubeconfigInjectable),
|
||||
}),
|
||||
});
|
||||
+2
-4
@@ -20,7 +20,7 @@ import directoryForUserDataInjectable from "../../../common/app-paths/directory-
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import type { PodStore } from "../+workloads-pods/store";
|
||||
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
describe("kube-object-list-layout", () => {
|
||||
let di: DiContainer;
|
||||
@@ -34,9 +34,7 @@ describe("kube-object-list-layout", () => {
|
||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectionToken);
|
||||
|
||||
di.override(hostedClusterInjectable, () => createCluster({
|
||||
di.override(hostedClusterInjectable, () => new Cluster({
|
||||
contextName: "some-context-name",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import activeEntityInternalClusterInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable";
|
||||
|
||||
const clusterNameInjectable = getInjectable({
|
||||
id: "cluster-name",
|
||||
instantiate: (di) => {
|
||||
const activeEntityInternalCluster = di.inject(activeEntityInternalClusterInjectable);
|
||||
|
||||
return computed(() => activeEntityInternalCluster.get()?.name.get());
|
||||
},
|
||||
});
|
||||
|
||||
export default clusterNameInjectable;
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import clusterInjectable from "./cluster.injectable";
|
||||
|
||||
const clusterNameInjectable = getInjectable({
|
||||
id: "cluster-name",
|
||||
|
||||
instantiate: (di) => {
|
||||
const cluster = di.inject(clusterInjectable);
|
||||
|
||||
return cluster?.name;
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
});
|
||||
|
||||
export default clusterNameInjectable;
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import getActiveClusterEntityInjectable from "../../../api/catalog/entity/get-active-cluster-entity.injectable";
|
||||
|
||||
const clusterInjectable = getInjectable({
|
||||
id: "cluster",
|
||||
instantiate: (di) => {
|
||||
const getActiveClusterEntity = di.inject(getActiveClusterEntityInjectable);
|
||||
|
||||
return getActiveClusterEntity();
|
||||
},
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
});
|
||||
|
||||
export default clusterInjectable;
|
||||
@@ -15,7 +15,6 @@ import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import { computed, runInAction } from "mobx";
|
||||
import clusterInjectable from "./dependencies/cluster.injectable";
|
||||
import type { DiRender } from "../test-utils/renderFor";
|
||||
import { renderFor } from "../test-utils/renderFor";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
@@ -25,6 +24,7 @@ import { KubeObjectMenu } from "./index";
|
||||
import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable";
|
||||
import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable";
|
||||
import { kubeObjectMenuItemInjectionToken } from "./kube-object-menu-item-injection-token";
|
||||
import activeEntityInternalClusterInjectable from "../../api/catalog/entity/get-active-cluster-entity.injectable";
|
||||
|
||||
// TODO: make `animated={false}` not required to make tests deterministic
|
||||
describe("kube-object-menu", () => {
|
||||
@@ -45,11 +45,10 @@ describe("kube-object-menu", () => {
|
||||
render = renderFor(di);
|
||||
|
||||
di.override(
|
||||
clusterInjectable,
|
||||
() =>
|
||||
({
|
||||
name: "Some name",
|
||||
} as Cluster),
|
||||
activeEntityInternalClusterInjectable,
|
||||
() => computed(() => ({
|
||||
name: computed(() => "Some name"),
|
||||
} as Cluster)),
|
||||
);
|
||||
|
||||
di.override(
|
||||
@@ -66,7 +65,11 @@ describe("kube-object-menu", () => {
|
||||
});
|
||||
|
||||
it("given no cluster, does not crash", () => {
|
||||
di.override(clusterInjectable, () => null);
|
||||
|
||||
di.override(
|
||||
activeEntityInternalClusterInjectable,
|
||||
() => computed(() => undefined),
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
render(<KubeObjectMenu object={null as never} toolbar={true} />);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { MenuItem, MenuActions } from "../menu";
|
||||
import identity from "lodash/identity";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import clusterNameInjectable from "./dependencies/cluster-name.injectable";
|
||||
import clusterNameInjectable from "./cluster-name.injectable";
|
||||
import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable";
|
||||
import kubeObjectMenuItemsInjectable from "./kube-object-menu-items.injectable";
|
||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||
@@ -38,7 +38,7 @@ export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends Men
|
||||
interface Dependencies {
|
||||
apiManager: ApiManager;
|
||||
kubeObjectMenuItems: IComputedValue<React.ElementType[]>;
|
||||
clusterName: string | undefined;
|
||||
clusterName: IComputedValue<string | undefined>;
|
||||
hideDetails: HideDetails;
|
||||
createEditResourceTab: (kubeObject: KubeObject) => void;
|
||||
onContextMenuOpen: OnKubeObjectContextMenuOpen;
|
||||
@@ -65,7 +65,7 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
|
||||
{`Remove ${object.kind} `}
|
||||
<b>{breadcrumb}</b>
|
||||
{" from "}
|
||||
<b>{this.props.clusterName}</b>
|
||||
<b>{this.props.clusterName.get()}</b>
|
||||
?
|
||||
</p>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ import navigateToPreferencesInjectable from "../../../features/preferences/commo
|
||||
import type { NavigateToHelmCharts } from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
|
||||
import navigateToHelmChartsInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { NamespaceStore } from "../+namespaces/store";
|
||||
import historyInjectable from "../../navigation/history.injectable";
|
||||
import type { MinimalTrayMenuItem } from "../../../main/tray/electron-tray/electron-tray.injectable";
|
||||
@@ -228,8 +228,6 @@ export const getApplicationBuilder = () => {
|
||||
},
|
||||
}));
|
||||
|
||||
const allowedResourcesState = observable.set<string>();
|
||||
|
||||
const windowHelpers = new Map<string, { di: DiContainer; getRendered: () => RenderResult }>();
|
||||
|
||||
const createElectronWindowFake: CreateElectronWindow = (configuration) => {
|
||||
@@ -526,19 +524,20 @@ export const getApplicationBuilder = () => {
|
||||
environment = environments.clusterFrame;
|
||||
|
||||
builder.beforeWindowStart((windowDi) => {
|
||||
const clusterStub = {
|
||||
const cluster = new Cluster({
|
||||
id: "some-cluster-id",
|
||||
accessibleNamespaces: observable.array(),
|
||||
allowedNamespaces: observable.array(),
|
||||
shouldShowResource: (kind) => allowedResourcesState.has(formatKubeApiResource(kind)),
|
||||
} as Partial<Cluster> as Cluster;
|
||||
contextName: "some-context-name",
|
||||
kubeConfigPath: "/some-path-to-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:12345",
|
||||
});
|
||||
|
||||
windowDi.override(activeKubernetesClusterInjectable, () =>
|
||||
computed(() => catalogEntityFromCluster(clusterStub)),
|
||||
computed(() => catalogEntityFromCluster(cluster)),
|
||||
);
|
||||
|
||||
windowDi.override(hostedClusterIdInjectable, () => clusterStub.id);
|
||||
windowDi.override(hostedClusterInjectable, () => clusterStub);
|
||||
windowDi.override(hostedClusterIdInjectable, () => cluster.id);
|
||||
windowDi.override(hostedClusterInjectable, () => cluster);
|
||||
|
||||
// TODO: Figure out a way to remove this stub.
|
||||
windowDi.override(namespaceStoreInjectable, () => ({
|
||||
@@ -645,8 +644,11 @@ export const getApplicationBuilder = () => {
|
||||
allowKubeResource: (resource) => {
|
||||
environment.onAllowKubeResource();
|
||||
|
||||
const windowDi = builder.applicationWindow.only.di;
|
||||
const cluster = windowDi.inject(hostedClusterInjectable);
|
||||
|
||||
runInAction(() => {
|
||||
allowedResourcesState.add(formatKubeApiResource(resource));
|
||||
cluster?.resourcesToShow.add(formatKubeApiResource(resource));
|
||||
});
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -14,8 +14,7 @@ import { DefaultProps } from "../../mui-base-theme";
|
||||
import { ClusterFrame } from "./cluster-frame";
|
||||
import historyInjectable from "../../navigation/history.injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||
import { Cluster } from "../../../common/cluster/cluster";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||
@@ -49,9 +48,7 @@ describe("<ClusterFrame />", () => {
|
||||
|
||||
testUsingFakeTime("2000-01-01 12:00:00am");
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
cluster = createCluster(
|
||||
cluster = new Cluster(
|
||||
{
|
||||
contextName: "my-cluster",
|
||||
id: "123456",
|
||||
@@ -68,8 +65,7 @@ describe("<ClusterFrame />", () => {
|
||||
|
||||
describe("given cluster with list nodes and namespaces permissions", () => {
|
||||
beforeEach(() => {
|
||||
// TODO: replace with not using private info
|
||||
(cluster as unknown as { readonly allowedResources: Cluster["allowedResources"] }).allowedResources.replace(["nodes", "namespaces"]);
|
||||
cluster.resourcesToShow.replace(["nodes", "namespaces"]);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
@@ -110,7 +106,7 @@ describe("<ClusterFrame />", () => {
|
||||
|
||||
describe("given cluster without list nodes, but with namespaces permissions", () => {
|
||||
beforeEach(() => {
|
||||
(cluster as unknown as { readonly allowedResources: Cluster["allowedResources"] }).allowedResources.replace(["namespaces"]);
|
||||
cluster.resourcesToShow.replace(["namespaces"]);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ export const initClusterFrame = ({
|
||||
);
|
||||
|
||||
await requestSetClusterFrameId(hostedCluster.id);
|
||||
await hostedCluster.whenReady; // cluster.activate() is done at this point
|
||||
await when(() => hostedCluster.ready.get()); // cluster.activate() is done at this point
|
||||
|
||||
catalogEntityRegistry.activeEntity = hostedCluster.id;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ const listNamespacesForbiddenHandlerInjectable = getInjectable({
|
||||
<b>Add Accessible Namespaces</b>
|
||||
<p>
|
||||
{"Cluster "}
|
||||
<b>{getClusterById(clusterId)?.name ?? "<unknown cluster>"}</b>
|
||||
<b>{getClusterById(clusterId)?.name.get() ?? "<unknown cluster>"}</b>
|
||||
{" does not have permissions to list namespaces. Please add the namespaces you have access to."}
|
||||
</p>
|
||||
<div className="flex gaps row align-left box grow">
|
||||
|
||||
Reference in new issue
Block a user