mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix allowed resources checks on GKE (#6657)
* Add check for incomplete SelfSubjectRulesReview to fix GKE Signed-off-by: Sebastian Malton <sebastian@malton.name> * Adding namespaced for KubeApiResource Signed-off-by: Sebastian Malton <sebastian@malton.name> * Refactoring of AuthorizationNamespaceReview Signed-off-by: Sebastian Malton <sebastian@malton.name> * Removing dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Refactoring ListApiResources Signed-off-by: Sebastian Malton <sebastian@malton.name> * Extract ClusterContext into deps for KubeObjectStore to fix circular import Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix remaining type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crash in frame by consolidating setup into runnables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors and remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix core resources not showing up Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix namespaces not being shown Signed-off-by: Sebastian Malton <sebastian@malton.name> * Simplify ClusterContext to remove something only NamespaceStore needs Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make sure the public API doesn't change Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixing namespace-select-filter tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix other tests requiring overrides Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix kludge in cluster-frame tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix remaining test failures Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix integration test due to incorrect casting Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix integration test and kube watches not working at all Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix secret details test Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix non-ApplicationBuilder tests by adding overrides Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crash due to trying to read hostedCluster too soon Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crash due to timing issues - Make injectable phases more explicit for renderer Signed-off-by: Sebastian Malton <sebastian@malton.name> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
e36f3d2d70
commit
443081493b
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import type { KubeApiResourceDescriptor } from "../rbac";
|
||||||
|
|
||||||
export const allowedResourcesInjectionToken = getInjectionToken<IComputedValue<Set<string>>>({
|
export const shouldShowResourceInjectionToken = getInjectionToken<IComputedValue<boolean>, KubeApiResourceDescriptor>({
|
||||||
id: "allowed-resources",
|
id: "should-show-resource",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,87 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { KubeConfig } from "@kubernetes/client-node";
|
|
||||||
import { AuthorizationV1Api } from "@kubernetes/client-node";
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import type { Logger } from "../logger";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import type { KubeApiResource } from "../rbac";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests the permissions for actions on the kube cluster
|
|
||||||
* @param namespace The namespace of the resources
|
|
||||||
* @param availableResources List of available resources in the cluster to resolve glob values fir api groups
|
|
||||||
* @returns list of allowed resources names
|
|
||||||
*/
|
|
||||||
export type RequestNamespaceResources = (namespace: string, availableResources: KubeApiResource[]) => Promise<string[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
|
|
||||||
*/
|
|
||||||
export type AuthorizationNamespaceReview = (proxyConfig: KubeConfig) => RequestNamespaceResources;
|
|
||||||
|
|
||||||
interface Dependencies {
|
|
||||||
logger: Logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
const authorizationNamespaceReview = ({ logger }: Dependencies): AuthorizationNamespaceReview => {
|
|
||||||
return (proxyConfig) => {
|
|
||||||
|
|
||||||
const api = proxyConfig.makeApiClient(AuthorizationV1Api);
|
|
||||||
|
|
||||||
return async (namespace, availableResources) => {
|
|
||||||
try {
|
|
||||||
const { body } = await api.createSelfSubjectRulesReview({
|
|
||||||
apiVersion: "authorization.k8s.io/v1",
|
|
||||||
kind: "SelfSubjectRulesReview",
|
|
||||||
spec: { namespace },
|
|
||||||
});
|
|
||||||
|
|
||||||
const resources = new Set<string>();
|
|
||||||
|
|
||||||
body.status?.resourceRules.forEach(resourceRule => {
|
|
||||||
if (!resourceRule.verbs.some(verb => ["*", "list"].includes(verb)) || !resourceRule.resources) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiGroups = resourceRule.apiGroups;
|
|
||||||
|
|
||||||
if (resourceRule.resources.length === 1 && resourceRule.resources[0] === "*" && apiGroups) {
|
|
||||||
if (apiGroups[0] === "*") {
|
|
||||||
availableResources.forEach(resource => resources.add(resource.apiName));
|
|
||||||
} else {
|
|
||||||
availableResources.forEach((apiResource)=> {
|
|
||||||
if (apiGroups.includes(apiResource.group || "")) {
|
|
||||||
resources.add(apiResource.apiName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resourceRule.resources.forEach(resource => resources.add(resource));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return [...resources];
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review: ${error}`, { namespace });
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const authorizationNamespaceReviewInjectable = getInjectable({
|
|
||||||
id: "authorization-namespace-review",
|
|
||||||
instantiate: (di) => {
|
|
||||||
const logger = di.inject(loggerInjectable);
|
|
||||||
|
|
||||||
return authorizationNamespaceReview({ logger });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default authorizationNamespaceReviewInjectable;
|
|
||||||
@ -9,8 +9,8 @@ import type { KubeConfig } from "@kubernetes/client-node";
|
|||||||
import { HttpError } from "@kubernetes/client-node";
|
import { HttpError } from "@kubernetes/client-node";
|
||||||
import type { Kubectl } from "../../main/kubectl/kubectl";
|
import type { Kubectl } from "../../main/kubectl/kubectl";
|
||||||
import type { KubeconfigManager } from "../../main/kubeconfig-manager/kubeconfig-manager";
|
import type { KubeconfigManager } from "../../main/kubeconfig-manager/kubeconfig-manager";
|
||||||
import type { KubeApiResource, KubeResource } from "../rbac";
|
import type { KubeApiResource, KubeApiResourceDescriptor } from "../rbac";
|
||||||
import { apiResourceRecord, apiResources } from "../rbac";
|
import { formatKubeApiResource } from "../rbac";
|
||||||
import type { VersionDetector } from "../../main/cluster-detectors/version-detector";
|
import type { VersionDetector } from "../../main/cluster-detectors/version-detector";
|
||||||
import type { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
|
import type { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
|
||||||
import plimit from "p-limit";
|
import plimit from "p-limit";
|
||||||
@ -25,8 +25,8 @@ import assert from "assert";
|
|||||||
import type { Logger } from "../logger";
|
import type { Logger } from "../logger";
|
||||||
import type { BroadcastMessage } from "../ipc/broadcast-message.injectable";
|
import type { BroadcastMessage } from "../ipc/broadcast-message.injectable";
|
||||||
import type { LoadConfigfromFile } from "../kube-helpers/load-config-from-file.injectable";
|
import type { LoadConfigfromFile } from "../kube-helpers/load-config-from-file.injectable";
|
||||||
import type { RequestNamespaceResources } from "./authorization-namespace-review.injectable";
|
import type { CanListResource, RequestNamespaceListPermissions, RequestNamespaceListPermissionsFor } from "./request-namespace-list-permissions.injectable";
|
||||||
import type { RequestListApiResources } from "./list-api-resources.injectable";
|
import type { RequestApiResources } from "./request-api-resources.injectable";
|
||||||
|
|
||||||
export interface ClusterDependencies {
|
export interface ClusterDependencies {
|
||||||
readonly directoryForKubeConfigs: string;
|
readonly directoryForKubeConfigs: string;
|
||||||
@ -36,8 +36,8 @@ export interface ClusterDependencies {
|
|||||||
createContextHandler: (cluster: Cluster) => ClusterContextHandler;
|
createContextHandler: (cluster: Cluster) => ClusterContextHandler;
|
||||||
createKubectl: (clusterVersion: string) => Kubectl;
|
createKubectl: (clusterVersion: string) => Kubectl;
|
||||||
createAuthorizationReview: (config: KubeConfig) => CanI;
|
createAuthorizationReview: (config: KubeConfig) => CanI;
|
||||||
createAuthorizationNamespaceReview: (config: KubeConfig) => RequestNamespaceResources;
|
requestApiResources: RequestApiResources;
|
||||||
createListApiResources: (cluster: Cluster) => RequestListApiResources;
|
requestNamespaceListPermissionsFor: RequestNamespaceListPermissionsFor;
|
||||||
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
||||||
createVersionDetector: (cluster: Cluster) => VersionDetector;
|
createVersionDetector: (cluster: Cluster) => VersionDetector;
|
||||||
broadcastMessage: BroadcastMessage;
|
broadcastMessage: BroadcastMessage;
|
||||||
@ -49,7 +49,7 @@ export interface ClusterDependencies {
|
|||||||
*
|
*
|
||||||
* @beta
|
* @beta
|
||||||
*/
|
*/
|
||||||
export class Cluster implements ClusterModel, ClusterState {
|
export class Cluster implements ClusterModel {
|
||||||
/** Unique id for a cluster */
|
/** Unique id for a cluster */
|
||||||
public readonly id: ClusterId;
|
public readonly id: ClusterId;
|
||||||
private kubeCtl: Kubectl | undefined;
|
private kubeCtl: Kubectl | undefined;
|
||||||
@ -62,7 +62,6 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
protected readonly _proxyKubeconfigManager: KubeconfigManager | undefined;
|
protected readonly _proxyKubeconfigManager: KubeconfigManager | undefined;
|
||||||
protected readonly eventsDisposer = disposer();
|
protected readonly eventsDisposer = disposer();
|
||||||
protected activated = false;
|
protected activated = false;
|
||||||
private readonly resourceAccessStatuses = new Map<KubeApiResource, boolean>();
|
|
||||||
|
|
||||||
public get contextHandler() {
|
public get contextHandler() {
|
||||||
// TODO: remove these once main/renderer are seperate classes
|
// TODO: remove these once main/renderer are seperate classes
|
||||||
@ -163,25 +162,21 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
* @observable
|
* @observable
|
||||||
*/
|
*/
|
||||||
@observable metadata: ClusterMetadata = {};
|
@observable metadata: ClusterMetadata = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of allowed namespaces verified via K8S::SelfSubjectAccessReview api
|
* List of allowed namespaces verified via K8S::SelfSubjectAccessReview api
|
||||||
*
|
|
||||||
* @observable
|
|
||||||
*/
|
*/
|
||||||
@observable allowedNamespaces: string[] = [];
|
readonly allowedNamespaces = observable.array<string>();
|
||||||
/**
|
|
||||||
* List of allowed resources
|
|
||||||
*
|
|
||||||
* @observable
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
@observable allowedResources: string[] = [];
|
|
||||||
/**
|
/**
|
||||||
* List of accessible namespaces provided by user in the Cluster Settings
|
* List of accessible namespaces provided by user in the Cluster Settings
|
||||||
*
|
|
||||||
* @observable
|
|
||||||
*/
|
*/
|
||||||
@observable accessibleNamespaces: string[] = [];
|
readonly accessibleNamespaces = observable.array<string>();
|
||||||
|
|
||||||
|
private readonly knownResources = observable.array<KubeApiResource>();
|
||||||
|
|
||||||
|
// The formatting of this is `group.name` or `name` (if in core)
|
||||||
|
private readonly allowedResources = observable.set<string>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Labels for the catalog entity
|
* Labels for the catalog entity
|
||||||
@ -299,7 +294,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (model.accessibleNamespaces) {
|
if (model.accessibleNamespaces) {
|
||||||
this.accessibleNamespaces = model.accessibleNamespaces;
|
this.accessibleNamespaces.replace(model.accessibleNamespaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.labels) {
|
if (model.labels) {
|
||||||
@ -433,8 +428,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
this.accessible = false;
|
this.accessible = false;
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.activated = false;
|
this.activated = false;
|
||||||
this.allowedNamespaces = [];
|
this.allowedNamespaces.clear();
|
||||||
this.resourceAccessStatuses.clear();
|
|
||||||
this.dependencies.logger.info(`[CLUSTER]: disconnected`, { id: this.id });
|
this.dependencies.logger.info(`[CLUSTER]: disconnected`, { id: this.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,8 +468,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
this.dependencies.logger.info(`[CLUSTER]: refreshAccessibility`, this.getMeta());
|
this.dependencies.logger.info(`[CLUSTER]: refreshAccessibility`, this.getMeta());
|
||||||
const proxyConfig = await this.getProxyKubeconfig();
|
const proxyConfig = await this.getProxyKubeconfig();
|
||||||
const canI = this.dependencies.createAuthorizationReview(proxyConfig);
|
const canI = this.dependencies.createAuthorizationReview(proxyConfig);
|
||||||
const requestNamespaceResources = this.dependencies.createAuthorizationNamespaceReview(proxyConfig);
|
const requestNamespaceListPermissions = this.dependencies.requestNamespaceListPermissionsFor(proxyConfig);
|
||||||
const listApiResources = this.dependencies.createListApiResources(this);
|
|
||||||
|
|
||||||
this.isAdmin = await canI({
|
this.isAdmin = await canI({
|
||||||
namespace: "kube-system",
|
namespace: "kube-system",
|
||||||
@ -486,8 +479,9 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
verb: "watch",
|
verb: "watch",
|
||||||
resource: "*",
|
resource: "*",
|
||||||
});
|
});
|
||||||
this.allowedNamespaces = await this.getAllowedNamespaces(proxyConfig);
|
this.allowedNamespaces.replace(await this.requestAllowedNamespaces(proxyConfig));
|
||||||
this.allowedResources = await this.getAllowedResources(listApiResources, requestNamespaceResources);
|
this.knownResources.replace(await this.dependencies.requestApiResources(this));
|
||||||
|
this.allowedResources.replace(await this.getAllowedResources(requestNamespaceListPermissions));
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +594,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
accessible: this.accessible,
|
accessible: this.accessible,
|
||||||
isAdmin: this.isAdmin,
|
isAdmin: this.isAdmin,
|
||||||
allowedNamespaces: this.allowedNamespaces,
|
allowedNamespaces: this.allowedNamespaces,
|
||||||
allowedResources: this.allowedResources,
|
allowedResources: [...this.allowedResources],
|
||||||
isGlobalWatchEnabled: this.isGlobalWatchEnabled,
|
isGlobalWatchEnabled: this.isGlobalWatchEnabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -611,8 +605,8 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
*/
|
*/
|
||||||
@action setState(state: ClusterState) {
|
@action setState(state: ClusterState) {
|
||||||
this.accessible = state.accessible;
|
this.accessible = state.accessible;
|
||||||
this.allowedNamespaces = state.allowedNamespaces;
|
this.allowedNamespaces.replace(state.allowedNamespaces);
|
||||||
this.allowedResources = state.allowedResources;
|
this.allowedResources.replace(state.allowedResources);
|
||||||
this.apiUrl = state.apiUrl;
|
this.apiUrl = state.apiUrl;
|
||||||
this.disconnected = state.disconnected;
|
this.disconnected = state.disconnected;
|
||||||
this.isAdmin = state.isAdmin;
|
this.isAdmin = state.isAdmin;
|
||||||
@ -644,7 +638,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
this.dependencies.broadcastMessage(`cluster:${this.id}:connection-update`, update);
|
this.dependencies.broadcastMessage(`cluster:${this.id}:connection-update`, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getAllowedNamespaces(proxyConfig: KubeConfig) {
|
protected async requestAllowedNamespaces(proxyConfig: KubeConfig) {
|
||||||
if (this.accessibleNamespaces.length) {
|
if (this.accessibleNamespaces.length) {
|
||||||
return this.accessibleNamespaces;
|
return this.accessibleNamespaces;
|
||||||
}
|
}
|
||||||
@ -668,69 +662,28 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getAllowedResources(listApiResources:RequestListApiResources, requestNamespaceResources: RequestNamespaceResources) {
|
protected async getAllowedResources(requestNamespaceListPermissions: RequestNamespaceListPermissions) {
|
||||||
try {
|
|
||||||
if (!this.allowedNamespaces.length) {
|
if (!this.allowedNamespaces.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const unknownResources = new Map<string, KubeApiResource>(apiResources.map(resource => ([resource.apiName, resource])));
|
try {
|
||||||
|
|
||||||
const availableResources = await listApiResources();
|
|
||||||
const availableResourcesNames = new Set(availableResources.map(apiResource => apiResource.apiName));
|
|
||||||
|
|
||||||
[...unknownResources.values()].map(unknownResource => {
|
|
||||||
if (!availableResourcesNames.has(unknownResource.apiName)) {
|
|
||||||
this.resourceAccessStatuses.set(unknownResource, false);
|
|
||||||
unknownResources.delete(unknownResource.apiName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (unknownResources.size > 0) {
|
|
||||||
const apiLimit = plimit(5); // 5 concurrent api requests
|
const apiLimit = plimit(5); // 5 concurrent api requests
|
||||||
|
const canListResourceCheckers = await Promise.all((
|
||||||
|
this.allowedNamespaces.map(namespace => apiLimit(() => requestNamespaceListPermissions(namespace)))
|
||||||
|
));
|
||||||
|
const canListNamespacedResource: CanListResource = (resource) => canListResourceCheckers.some(fn => fn(resource));
|
||||||
|
|
||||||
await Promise.all(this.allowedNamespaces.map(namespace => apiLimit(async () => {
|
return this.knownResources
|
||||||
if (unknownResources.size === 0) {
|
.filter(canListNamespacedResource)
|
||||||
return;
|
.map(formatKubeApiResource);
|
||||||
}
|
|
||||||
|
|
||||||
const namespaceResources = await requestNamespaceResources(namespace, availableResources);
|
|
||||||
|
|
||||||
for (const resourceName of namespaceResources) {
|
|
||||||
const unknownResource = unknownResources.get(resourceName);
|
|
||||||
|
|
||||||
if (unknownResource) {
|
|
||||||
this.resourceAccessStatuses.set(unknownResource, true);
|
|
||||||
unknownResources.delete(resourceName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
|
|
||||||
for (const forbiddenResource of unknownResources.values()) {
|
|
||||||
this.resourceAccessStatuses.set(forbiddenResource, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiResources
|
|
||||||
.filter((resource) => this.resourceAccessStatuses.get(resource))
|
|
||||||
.map(apiResource => apiResource.apiName);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isAllowedResource(kind: string): boolean {
|
shouldShowResource(resource: KubeApiResourceDescriptor): boolean {
|
||||||
if ((kind as KubeResource) in apiResourceRecord) {
|
return this.allowedResources.has(formatKubeApiResource(resource));
|
||||||
return this.allowedResources.includes(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiResource = apiResources.find(resource => resource.kind === kind);
|
|
||||||
|
|
||||||
if (apiResource) {
|
|
||||||
return this.allowedResources.includes(apiResource.apiName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // allowed by default for other resources
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isMetricHidden(resource: ClusterMetricsResourceType): boolean {
|
isMetricHidden(resource: ClusterMetricsResourceType): boolean {
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import type { KubeResource } from "../rbac";
|
|
||||||
import { apiResourceRecord, apiResources } from "../rbac";
|
|
||||||
|
|
||||||
export const isAllowedResource = (allowedResources: string[]) => (kind: string): boolean => {
|
|
||||||
if ((kind as KubeResource) in apiResourceRecord) {
|
|
||||||
return allowedResources.includes(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiResource = apiResources.find(resource => resource.kind === kind);
|
|
||||||
|
|
||||||
if (apiResource) {
|
|
||||||
return allowedResources.includes(apiResource.apiName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // allowed by default for other resources
|
|
||||||
};
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type {
|
|
||||||
V1APIGroupList,
|
|
||||||
V1APIResourceList,
|
|
||||||
V1APIVersions,
|
|
||||||
} from "@kubernetes/client-node";
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import type { K8sRequest } from "../../main/k8s-request.injectable";
|
|
||||||
import k8SRequestInjectable from "../../main/k8s-request.injectable";
|
|
||||||
import type { Logger } from "../logger";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import type { KubeApiResource, KubeResource } from "../rbac";
|
|
||||||
import type { Cluster } from "./cluster";
|
|
||||||
import plimit from "p-limit";
|
|
||||||
|
|
||||||
export type RequestListApiResources = () => Promise<KubeApiResource[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
|
|
||||||
*/
|
|
||||||
export type ListApiResources = (cluster: Cluster) => RequestListApiResources;
|
|
||||||
|
|
||||||
interface Dependencies {
|
|
||||||
logger: Logger;
|
|
||||||
k8sRequest: K8sRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listApiResources = ({ k8sRequest, logger }: Dependencies): ListApiResources => {
|
|
||||||
return (cluster) => {
|
|
||||||
const clusterRequest = (path: string) => k8sRequest(cluster, path);
|
|
||||||
const apiLimit = plimit(5);
|
|
||||||
|
|
||||||
return async () => {
|
|
||||||
const resources: KubeApiResource[] = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const resourceListGroups:{ group:string;path:string }[] = [];
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
[
|
|
||||||
clusterRequest("/api").then((response:V1APIVersions)=>response.versions.forEach(version => resourceListGroups.push({ group:version, path:`/api/${version}` }))),
|
|
||||||
clusterRequest("/apis").then((response:V1APIGroupList) => response.groups.forEach(group => {
|
|
||||||
const preferredVersion = group.preferredVersion?.groupVersion;
|
|
||||||
|
|
||||||
if (preferredVersion) {
|
|
||||||
resourceListGroups.push({ group:group.name, path:`/apis/${preferredVersion}` });
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
resourceListGroups.map(({ group, path }) => apiLimit(async () => {
|
|
||||||
const apiResources:V1APIResourceList = await clusterRequest(path);
|
|
||||||
|
|
||||||
if (apiResources.resources) {
|
|
||||||
resources.push(
|
|
||||||
...apiResources.resources.filter(resource => resource.verbs.includes("list")).map((resource) => ({
|
|
||||||
apiName: resource.name as KubeResource,
|
|
||||||
kind: resource.kind,
|
|
||||||
group,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`[LIST-API-RESOURCES]: failed to list api resources: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resources;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const listApiResourcesInjectable = getInjectable({
|
|
||||||
id: "list-api-resources",
|
|
||||||
instantiate: (di) => {
|
|
||||||
const k8sRequest = di.inject(k8SRequestInjectable);
|
|
||||||
const logger = di.inject(loggerInjectable);
|
|
||||||
|
|
||||||
return listApiResources({ k8sRequest, logger });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default listApiResourcesInjectable;
|
|
||||||
83
src/common/cluster/request-api-resources.injectable.ts
Normal file
83
src/common/cluster/request-api-resources.injectable.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { V1APIGroupList, V1APIResourceList, V1APIVersions } from "@kubernetes/client-node";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import k8SRequestInjectable from "../../main/k8s-request.injectable";
|
||||||
|
import loggerInjectable from "../logger.injectable";
|
||||||
|
import type { KubeApiResource } from "../rbac";
|
||||||
|
import type { Cluster } from "./cluster";
|
||||||
|
import plimit from "p-limit";
|
||||||
|
|
||||||
|
export type RequestApiResources = (cluster: Cluster) => Promise<KubeApiResource[]>;
|
||||||
|
|
||||||
|
interface KubeResourceListGroup {
|
||||||
|
group: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestApiResourcesInjectable = getInjectable({
|
||||||
|
id: "request-api-resources",
|
||||||
|
instantiate: (di): RequestApiResources => {
|
||||||
|
const k8sRequest = di.inject(k8SRequestInjectable);
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
|
||||||
|
return async (cluster) => {
|
||||||
|
const apiLimit = plimit(5);
|
||||||
|
const kubeApiResources: KubeApiResource[] = [];
|
||||||
|
const resourceListGroups: KubeResourceListGroup[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
|
const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
|
||||||
|
|
||||||
|
for (const version of versions) {
|
||||||
|
resourceListGroups.push({
|
||||||
|
group: version,
|
||||||
|
path: `/api/${version}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
const { groups } = await k8sRequest(cluster, "/apis") as V1APIGroupList;
|
||||||
|
|
||||||
|
for (const { preferredVersion, name } of groups) {
|
||||||
|
const { groupVersion } = preferredVersion ?? {};
|
||||||
|
|
||||||
|
if (groupVersion) {
|
||||||
|
resourceListGroups.push({
|
||||||
|
group: name,
|
||||||
|
path: `/apis/${groupVersion}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
resourceListGroups.map(({ group, path }) => apiLimit(async () => {
|
||||||
|
const { resources } = await k8sRequest(cluster, path) as V1APIResourceList;
|
||||||
|
|
||||||
|
for (const resource of resources) {
|
||||||
|
kubeApiResources.push({
|
||||||
|
apiName: resource.name,
|
||||||
|
kind: resource.kind,
|
||||||
|
group,
|
||||||
|
namespaced: resource.namespaced,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[LIST-API-RESOURCES]: failed to list api resources: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return kubeApiResources;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default requestApiResourcesInjectable;
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { KubeConfig } from "@kubernetes/client-node";
|
||||||
|
import { AuthorizationV1Api } from "@kubernetes/client-node";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import loggerInjectable from "../logger.injectable";
|
||||||
|
import type { KubeApiResource } from "../rbac";
|
||||||
|
|
||||||
|
export type CanListResource = (resource: KubeApiResource) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the permissions for actions on the kube cluster
|
||||||
|
* @param namespace The namespace of the resources
|
||||||
|
*/
|
||||||
|
export type RequestNamespaceListPermissions = (namespace: string) => Promise<CanListResource>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
|
||||||
|
*/
|
||||||
|
export type RequestNamespaceListPermissionsFor = (proxyConfig: KubeConfig) => RequestNamespaceListPermissions;
|
||||||
|
|
||||||
|
const requestNamespaceListPermissionsForInjectable = getInjectable({
|
||||||
|
id: "request-namespace-list-permissions-for",
|
||||||
|
instantiate: (di): RequestNamespaceListPermissionsFor => {
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
|
||||||
|
return (proxyConfig) => {
|
||||||
|
const api = proxyConfig.makeApiClient(AuthorizationV1Api);
|
||||||
|
|
||||||
|
return async (namespace) => {
|
||||||
|
try {
|
||||||
|
const { body: { status }} = await api.createSelfSubjectRulesReview({
|
||||||
|
apiVersion: "authorization.k8s.io/v1",
|
||||||
|
kind: "SelfSubjectRulesReview",
|
||||||
|
spec: { namespace },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!status || status.incomplete) {
|
||||||
|
logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
|
||||||
|
|
||||||
|
return () => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resourceRules } = status;
|
||||||
|
|
||||||
|
return (resource) => {
|
||||||
|
const resourceRule = resourceRules.find(({
|
||||||
|
apiGroups = [],
|
||||||
|
resources = [],
|
||||||
|
}) => {
|
||||||
|
const isAboutRelevantApiGroup = apiGroups.includes("*") || apiGroups.includes(resource.group);
|
||||||
|
const isAboutResource = resources.includes("*") || resources.includes(resource.apiName);
|
||||||
|
|
||||||
|
return isAboutRelevantApiGroup && isAboutResource;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resourceRule) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { verbs } = resourceRule;
|
||||||
|
|
||||||
|
return verbs.includes("*") || verbs.includes("list");
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error });
|
||||||
|
|
||||||
|
return () => true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default requestNamespaceListPermissionsForInjectable;
|
||||||
@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as Mobx from "mobx";
|
|
||||||
import * as Immer from "immer";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup default configuration for external npm-packages
|
|
||||||
*/
|
|
||||||
export default function configurePackages() {
|
|
||||||
// Docs: https://mobx.js.org/configuration.html
|
|
||||||
Mobx.configure({
|
|
||||||
enforceActions: "never",
|
|
||||||
|
|
||||||
// TODO: enable later (read more: https://mobx.js.org/migrating-from-4-or-5.html)
|
|
||||||
// computedRequiresReaction: true,
|
|
||||||
// reactionRequiresObservable: true,
|
|
||||||
// observableRequiresReaction: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Docs: https://immerjs.github.io/immer/
|
|
||||||
// Required in `utils/storage-helper.ts`
|
|
||||||
Immer.setAutoFreeze(false); // allow to merge mobx observables
|
|
||||||
Immer.enableMapSet(); // allow to merge maps and sets
|
|
||||||
}
|
|
||||||
@ -3,22 +3,19 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const configMapsRouteInjectable = getInjectable({
|
const configMapsRouteInjectable = getInjectable({
|
||||||
id: "config-maps-route",
|
id: "config-maps-route",
|
||||||
|
instantiate: (di) => ({
|
||||||
instantiate: (di) => {
|
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "configmaps");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/configmaps",
|
path: "/configmaps",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "configmaps",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const horizontalPodAutoscalersRouteInjectable = getInjectable({
|
const horizontalPodAutoscalersRouteInjectable = getInjectable({
|
||||||
id: "horizontal-pod-autoscalers-route",
|
id: "horizontal-pod-autoscalers-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "horizontalpodautoscalers");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/hpa",
|
path: "/hpa",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "horizontalpodautoscalers",
|
||||||
},
|
group: "autoscaling",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const leasesRouteInjectable = getInjectable({
|
const leasesRouteInjectable = getInjectable({
|
||||||
id: "leases",
|
id: "leases",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "leases");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/leases",
|
path: "/leases",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "leases",
|
||||||
},
|
group: "coordination.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,24 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const limitRangesRouteInjectable = getInjectable({
|
const limitRangesRouteInjectable = getInjectable({
|
||||||
id: "limit-ranges-route",
|
id: "limit-ranges-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const limitRangesIsAllowed = di.inject(
|
|
||||||
isAllowedResourceInjectable,
|
|
||||||
"limitranges",
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/limitranges",
|
path: "/limitranges",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: limitRangesIsAllowed,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "limitranges",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const podDisruptionBudgetsRouteInjectable = getInjectable({
|
const podDisruptionBudgetsRouteInjectable = getInjectable({
|
||||||
id: "pod-disruption-budgets-route",
|
id: "pod-disruption-budgets-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "poddisruptionbudgets");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/poddisruptionbudgets",
|
path: "/poddisruptionbudgets",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "poddisruptionbudgets",
|
||||||
},
|
group: "policy",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const priorityClassesRouteInjectable = getInjectable({
|
const priorityClassesRouteInjectable = getInjectable({
|
||||||
id: "priority-classes-route",
|
id: "priority-classes-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "priorityclasses");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/priorityclasses",
|
path: "/priorityclasses",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "priorityclasses",
|
||||||
},
|
group: "scheduling.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const resourceQuotasRouteInjectable = getInjectable({
|
const resourceQuotasRouteInjectable = getInjectable({
|
||||||
id: "resource-quotas-route",
|
id: "resource-quotas-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "resourcequotas");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/resourcequotas",
|
path: "/resourcequotas",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "resourcequotas",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const runtimeClassesRouteInjectable = getInjectable({
|
const runtimeClassesRouteInjectable = getInjectable({
|
||||||
id: "runtime-classes-route",
|
id: "runtime-classes-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "runtimeclasses");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/runtimeclasses",
|
path: "/runtimeclasses",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "runtimeclasses",
|
||||||
},
|
group: "node.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const secretsRouteInjectable = getInjectable({
|
const secretsRouteInjectable = getInjectable({
|
||||||
id: "secrets-route",
|
id: "secrets-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "secrets");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/secrets",
|
path: "/secrets",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "secrets",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const eventsRouteInjectable = getInjectable({
|
const eventsRouteInjectable = getInjectable({
|
||||||
id: "events-route",
|
id: "events-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "events");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/events",
|
path: "/events",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "events",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const namespacesRouteInjectable = getInjectable({
|
const namespacesRouteInjectable = getInjectable({
|
||||||
id: "namespaces-route",
|
id: "namespaces-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "namespaces");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/namespaces",
|
path: "/namespaces",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "namespaces",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const endpointsRouteInjectable = getInjectable({
|
const endpointsRouteInjectable = getInjectable({
|
||||||
id: "endpoints-route",
|
id: "endpoints-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "endpoints");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/endpoints",
|
path: "/endpoints",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "endpoints",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,27 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
|
import { computedOr } from "../../../../../utils/computed-or";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const ingressesRouteInjectable = getInjectable({
|
const ingressesRouteInjectable = getInjectable({
|
||||||
id: "ingresses-route",
|
id: "ingresses-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "ingresses");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/ingresses",
|
path: "/ingresses",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: computedOr(
|
||||||
};
|
di.inject(shouldShowResourceInjectionToken, {
|
||||||
},
|
apiName: "ingresses",
|
||||||
|
group: "networking.k8s.io",
|
||||||
|
}),
|
||||||
|
di.inject(shouldShowResourceInjectionToken, {
|
||||||
|
apiName: "ingresses",
|
||||||
|
group: "extensions",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const networkPoliciesRouteInjectable = getInjectable({
|
const networkPoliciesRouteInjectable = getInjectable({
|
||||||
id: "network-policies-route",
|
id: "network-policies-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "networkpolicies");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/network-policies",
|
path: "/network-policies",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "networkpolicies",
|
||||||
},
|
group: "networking.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const servicesRouteInjectable = getInjectable({
|
const servicesRouteInjectable = getInjectable({
|
||||||
id: "services-route",
|
id: "services-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "services");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/services",
|
path: "/services",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "services",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const nodesRouteInjectable = getInjectable({
|
const nodesRouteInjectable = getInjectable({
|
||||||
id: "nodes-route",
|
id: "nodes-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "nodes");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/nodes",
|
path: "/nodes",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "nodes",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const clusterOverviewRouteInjectable = getInjectable({
|
const clusterOverviewRouteInjectable = getInjectable({
|
||||||
id: "cluster-overview-route",
|
id: "cluster-overview-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "nodes");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/overview",
|
path: "/overview",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "nodes",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const persistentVolumeClaimsRouteInjectable = getInjectable({
|
const persistentVolumeClaimsRouteInjectable = getInjectable({
|
||||||
id: "persistent-volume-claims-route",
|
id: "persistent-volume-claims-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "persistentvolumeclaims");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/persistent-volume-claims",
|
path: "/persistent-volume-claims",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "persistentvolumeclaims",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const persistentVolumesRouteInjectable = getInjectable({
|
const persistentVolumesRouteInjectable = getInjectable({
|
||||||
id: "persistent-volumes-route",
|
id: "persistent-volumes-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "persistentvolumes");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/persistent-volumes",
|
path: "/persistent-volumes",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "persistentvolumes",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const storageClassesRouteInjectable = getInjectable({
|
const storageClassesRouteInjectable = getInjectable({
|
||||||
id: "storage-classes-route",
|
id: "storage-classes-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "storageclasses");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/storage-classes",
|
path: "/storage-classes",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "storageclasses",
|
||||||
},
|
group: "storage.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const clusterRoleBindingsRouteInjectable = getInjectable({
|
const clusterRoleBindingsRouteInjectable = getInjectable({
|
||||||
id: "cluster-role-bindings-route",
|
id: "cluster-role-bindings-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "clusterrolebindings");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/cluster-role-bindings",
|
path: "/cluster-role-bindings",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "clusterrolebindings",
|
||||||
},
|
group: "rbac.authorization.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const clusterRolesRouteInjectable = getInjectable({
|
const clusterRolesRouteInjectable = getInjectable({
|
||||||
id: "cluster-roles-route",
|
id: "cluster-roles-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "clusterroles");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/cluster-roles",
|
path: "/cluster-roles",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "clusterroles",
|
||||||
},
|
group: "rbac.authorization.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,19 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const podSecurityPoliciesRouteInjectable = getInjectable({
|
const podSecurityPoliciesRouteInjectable = getInjectable({
|
||||||
id: "pod-security-policies-route",
|
id: "pod-security-policies-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "podsecuritypolicies");
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: "/pod-security-policies",
|
path: "/pod-security-policies",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
|
apiName: "podsecuritypolicies",
|
||||||
|
group: "policy",
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -3,19 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const roleBindingsRouteInjectable = getInjectable({
|
const roleBindingsRouteInjectable = getInjectable({
|
||||||
id: "role-bindings-route",
|
id: "role-bindings-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "rolebindings");
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: "/role-bindings",
|
path: "/role-bindings",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
|
apiName: "rolebindings",
|
||||||
|
group: "rbac.authorization.k8s.io",
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const rolesRouteInjectable = getInjectable({
|
const rolesRouteInjectable = getInjectable({
|
||||||
id: "roles-route",
|
id: "roles-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "roles");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/roles",
|
path: "/roles",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "roles",
|
||||||
},
|
group: "rbac.authorization.k8s.io",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const serviceAccountsRouteInjectable = getInjectable({
|
const serviceAccountsRouteInjectable = getInjectable({
|
||||||
id: "service-accounts-route",
|
id: "service-accounts-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "serviceaccounts");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/service-accounts",
|
path: "/service-accounts",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "serviceaccounts",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const cronJobsRouteInjectable = getInjectable({
|
const cronJobsRouteInjectable = getInjectable({
|
||||||
id: "cron-jobs-route",
|
id: "cron-jobs-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "cronjobs");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/cronjobs",
|
path: "/cronjobs",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "cronjobs",
|
||||||
},
|
group: "batch",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const daemonsetsRouteInjectable = getInjectable({
|
const daemonsetsRouteInjectable = getInjectable({
|
||||||
id: "daemonsets-route",
|
id: "daemonsets-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "daemonsets");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/daemonsets",
|
path: "/daemonsets",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "daemonsets",
|
||||||
},
|
group: "apps",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const deploymentsRouteInjectable = getInjectable({
|
const deploymentsRouteInjectable = getInjectable({
|
||||||
id: "deployments-route",
|
id: "deployments-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "deployments");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/deployments",
|
path: "/deployments",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "deployments",
|
||||||
},
|
group: "apps",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const jobsRouteInjectable = getInjectable({
|
const jobsRouteInjectable = getInjectable({
|
||||||
id: "jobs-route",
|
id: "jobs-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "jobs");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/jobs",
|
path: "/jobs",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "jobs",
|
||||||
},
|
group: "batch",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const podsRouteInjectable = getInjectable({
|
const podsRouteInjectable = getInjectable({
|
||||||
id: "pods-route",
|
id: "pods-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "pods");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/pods",
|
path: "/pods",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "pods",
|
||||||
},
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const replicasetsRouteInjectable = getInjectable({
|
const replicasetsRouteInjectable = getInjectable({
|
||||||
id: "replicasets-route",
|
id: "replicasets-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "replicasets");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/replicasets",
|
path: "/replicasets",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "replicasets",
|
||||||
},
|
group: "apps",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,21 +3,20 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
|
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const statefulsetsRouteInjectable = getInjectable({
|
const statefulsetsRouteInjectable = getInjectable({
|
||||||
id: "statefulsets-route",
|
id: "statefulsets-route",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const isAllowedResource = di.inject(isAllowedResourceInjectable, "statefulsets");
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/statefulsets",
|
path: "/statefulsets",
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
isEnabled: isAllowedResource,
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
};
|
apiName: "statefulsets",
|
||||||
},
|
group: "apps",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,14 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
|
import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
|
||||||
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
|
||||||
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
|
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
|
||||||
|
import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||||
|
import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import type { ApiManager } from "../api-manager";
|
import type { ApiManager } from "../api-manager";
|
||||||
import apiManagerInjectable from "../api-manager/manager.injectable";
|
import apiManagerInjectable from "../api-manager/manager.injectable";
|
||||||
import { KubeApi } from "../kube-api";
|
import { KubeApi } from "../kube-api";
|
||||||
@ -22,9 +29,24 @@ class TestStore extends KubeObjectStore<KubeObject, TestApi> {
|
|||||||
|
|
||||||
describe("ApiManager", () => {
|
describe("ApiManager", () => {
|
||||||
let apiManager: ApiManager;
|
let apiManager: ApiManager;
|
||||||
|
let di: DiContainer;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||||
|
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||||
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
|
||||||
|
const createCluster = di.inject(createClusterInjectable);
|
||||||
|
|
||||||
|
di.override(hostedClusterInjectable, () => createCluster({
|
||||||
|
contextName: "some-context-name",
|
||||||
|
id: "some-cluster-id",
|
||||||
|
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||||
|
}, {
|
||||||
|
clusterServerUrl: "https://localhost:8080",
|
||||||
|
}));
|
||||||
|
|
||||||
apiManager = di.inject(apiManagerInjectable);
|
apiManager = di.inject(apiManagerInjectable);
|
||||||
});
|
});
|
||||||
@ -40,7 +62,9 @@ describe("ApiManager", () => {
|
|||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
});
|
});
|
||||||
const kubeStore = new TestStore(kubeApi);
|
const kubeStore = new TestStore({
|
||||||
|
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
|
}, kubeApi);
|
||||||
|
|
||||||
apiManager.registerApi(apiBase, kubeApi);
|
apiManager.registerApi(apiBase, kubeApi);
|
||||||
|
|
||||||
|
|||||||
@ -3,43 +3,23 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import type { KubeJsonApi } from "../kube-json-api";
|
import type { KubeJsonApi } from "../kube-json-api";
|
||||||
import { PassThrough } from "stream";
|
|
||||||
import type { ApiManager } from "../api-manager";
|
import type { ApiManager } from "../api-manager";
|
||||||
import { Ingress, IngressApi } from "../endpoints";
|
import { Ingress, IngressApi } from "../endpoints";
|
||||||
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
import apiManagerInjectable from "../api-manager/manager.injectable";
|
import apiManagerInjectable from "../api-manager/manager.injectable";
|
||||||
import autoRegistrationInjectable from "../api-manager/auto-registration.injectable";
|
|
||||||
import type { Fetch } from "../../fetch/fetch.injectable";
|
import type { Fetch } from "../../fetch/fetch.injectable";
|
||||||
import fetchInjectable from "../../fetch/fetch.injectable";
|
import fetchInjectable from "../../fetch/fetch.injectable";
|
||||||
import type { AsyncFnMock } from "@async-fn/jest";
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
import asyncFn from "@async-fn/jest";
|
import asyncFn from "@async-fn/jest";
|
||||||
import { flushPromises } from "../../test-utils/flush-promises";
|
import { flushPromises } from "../../test-utils/flush-promises";
|
||||||
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
|
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
|
||||||
import type { Response, Headers as NodeFetchHeaders } from "node-fetch";
|
import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable";
|
||||||
|
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
|
||||||
const createMockResponseFromString = (url: string, data: string, statusCode = 200) => {
|
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
|
||||||
const res: jest.Mocked<Response> = {
|
import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
|
import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
|
||||||
clone: jest.fn(() => res),
|
import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
|
||||||
arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
|
import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||||
blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
|
|
||||||
body: new PassThrough(),
|
|
||||||
bodyUsed: false,
|
|
||||||
headers: new Headers() as NodeFetchHeaders,
|
|
||||||
json: jest.fn(async () => JSON.parse(await res.text())),
|
|
||||||
ok: 200 <= statusCode && statusCode < 300,
|
|
||||||
redirected: 300 <= statusCode && statusCode < 400,
|
|
||||||
size: data.length,
|
|
||||||
status: statusCode,
|
|
||||||
statusText: "some-text",
|
|
||||||
text: jest.fn(async () => data),
|
|
||||||
type: "basic",
|
|
||||||
url,
|
|
||||||
formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
|
|
||||||
};
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("KubeApi", () => {
|
describe("KubeApi", () => {
|
||||||
let request: KubeJsonApi;
|
let request: KubeJsonApi;
|
||||||
@ -52,6 +32,20 @@ describe("KubeApi", () => {
|
|||||||
fetchMock = asyncFn();
|
fetchMock = asyncFn();
|
||||||
di.override(fetchInjectable, () => fetchMock);
|
di.override(fetchInjectable, () => fetchMock);
|
||||||
|
|
||||||
|
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||||
|
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||||
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
|
||||||
|
const createCluster = di.inject(createClusterInjectable);
|
||||||
|
|
||||||
|
di.override(hostedClusterInjectable, () => createCluster({
|
||||||
|
contextName: "some-context-name",
|
||||||
|
id: "some-cluster-id",
|
||||||
|
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||||
|
}, {
|
||||||
|
clusterServerUrl: "https://localhost:8080",
|
||||||
|
}));
|
||||||
|
|
||||||
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
|
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
|
||||||
|
|
||||||
request = createKubeJsonApi({
|
request = createKubeJsonApi({
|
||||||
@ -60,7 +54,9 @@ describe("KubeApi", () => {
|
|||||||
});
|
});
|
||||||
registerApiSpy = jest.spyOn(di.inject(apiManagerInjectable), "registerApi");
|
registerApiSpy = jest.spyOn(di.inject(apiManagerInjectable), "registerApi");
|
||||||
|
|
||||||
di.inject(autoRegistrationInjectable);
|
const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
|
||||||
|
|
||||||
|
setupAutoRegistration.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("on first call to IngressApi.get()", () => {
|
describe("on first call to IngressApi.get()", () => {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import type { KubeJsonApi, KubeJsonApiData } from "../kube-json-api";
|
|||||||
import { PassThrough } from "stream";
|
import { PassThrough } from "stream";
|
||||||
import { Deployment, DeploymentApi, NamespaceApi, Pod, PodApi } from "../endpoints";
|
import { Deployment, DeploymentApi, NamespaceApi, Pod, PodApi } from "../endpoints";
|
||||||
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
import autoRegistrationInjectable from "../api-manager/auto-registration.injectable";
|
|
||||||
import type { Fetch } from "../../fetch/fetch.injectable";
|
import type { Fetch } from "../../fetch/fetch.injectable";
|
||||||
import fetchInjectable from "../../fetch/fetch.injectable";
|
import fetchInjectable from "../../fetch/fetch.injectable";
|
||||||
import type { CreateKubeApiForRemoteCluster } from "../create-kube-api-for-remote-cluster.injectable";
|
import type { CreateKubeApiForRemoteCluster } from "../create-kube-api-for-remote-cluster.injectable";
|
||||||
@ -19,64 +18,14 @@ import { flushPromises } from "../../test-utils/flush-promises";
|
|||||||
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
|
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
|
||||||
import type { IKubeWatchEvent } from "../kube-watch-event";
|
import type { IKubeWatchEvent } from "../kube-watch-event";
|
||||||
import type { KubeJsonApiDataFor } from "../kube-object";
|
import type { KubeJsonApiDataFor } from "../kube-object";
|
||||||
import type { Response, Headers as NodeFetchHeaders } from "node-fetch";
|
|
||||||
import AbortController from "abort-controller";
|
import AbortController from "abort-controller";
|
||||||
|
import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable";
|
||||||
const createMockResponseFromString = (url: string, data: string, statusCode = 200) => {
|
import { createMockResponseFromStream, createMockResponseFromString } from "../../../test-utils/mock-responses";
|
||||||
const res: jest.Mocked<Response> = {
|
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
|
||||||
buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
|
import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
clone: jest.fn(() => res),
|
import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
|
||||||
arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
|
import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
|
||||||
blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
|
import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||||
body: new PassThrough(),
|
|
||||||
bodyUsed: false,
|
|
||||||
headers: new Headers() as NodeFetchHeaders,
|
|
||||||
json: jest.fn(async () => JSON.parse(await res.text())),
|
|
||||||
ok: 200 <= statusCode && statusCode < 300,
|
|
||||||
redirected: 300 <= statusCode && statusCode < 400,
|
|
||||||
size: data.length,
|
|
||||||
status: statusCode,
|
|
||||||
statusText: "some-text",
|
|
||||||
text: jest.fn(async () => data),
|
|
||||||
type: "basic",
|
|
||||||
url,
|
|
||||||
formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
|
|
||||||
};
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMockResponseFromStream = (url: string, stream: NodeJS.ReadableStream, statusCode = 200) => {
|
|
||||||
const res: jest.Mocked<Response> = {
|
|
||||||
buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
|
|
||||||
clone: jest.fn(() => res),
|
|
||||||
arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
|
|
||||||
blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
|
|
||||||
body: stream,
|
|
||||||
bodyUsed: false,
|
|
||||||
headers: new Headers() as NodeFetchHeaders,
|
|
||||||
json: jest.fn(async () => JSON.parse(await res.text())),
|
|
||||||
ok: 200 <= statusCode && statusCode < 300,
|
|
||||||
redirected: 300 <= statusCode && statusCode < 400,
|
|
||||||
size: 10,
|
|
||||||
status: statusCode,
|
|
||||||
statusText: "some-text",
|
|
||||||
text: jest.fn(() => {
|
|
||||||
const chunks: Buffer[] = [];
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
||||||
stream.on("error", (err) => reject(err));
|
|
||||||
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
type: "basic",
|
|
||||||
url,
|
|
||||||
formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
|
|
||||||
};
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("createKubeApiForRemoteCluster", () => {
|
describe("createKubeApiForRemoteCluster", () => {
|
||||||
let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster;
|
let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster;
|
||||||
@ -85,6 +34,20 @@ describe("createKubeApiForRemoteCluster", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||||
|
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||||
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
|
||||||
|
const createCluster = di.inject(createClusterInjectable);
|
||||||
|
|
||||||
|
di.override(hostedClusterInjectable, () => createCluster({
|
||||||
|
contextName: "some-context-name",
|
||||||
|
id: "some-cluster-id",
|
||||||
|
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||||
|
}, {
|
||||||
|
clusterServerUrl: "https://localhost:8080",
|
||||||
|
}));
|
||||||
|
|
||||||
fetchMock = asyncFn();
|
fetchMock = asyncFn();
|
||||||
di.override(fetchInjectable, () => fetchMock);
|
di.override(fetchInjectable, () => fetchMock);
|
||||||
|
|
||||||
@ -174,6 +137,20 @@ describe("KubeApi", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||||
|
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||||
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
|
||||||
|
const createCluster = di.inject(createClusterInjectable);
|
||||||
|
|
||||||
|
di.override(hostedClusterInjectable, () => createCluster({
|
||||||
|
contextName: "some-context-name",
|
||||||
|
id: "some-cluster-id",
|
||||||
|
kubeConfigPath: "/some-path-to-a-kubeconfig",
|
||||||
|
}, {
|
||||||
|
clusterServerUrl: "https://localhost:8080",
|
||||||
|
}));
|
||||||
|
|
||||||
fetchMock = asyncFn();
|
fetchMock = asyncFn();
|
||||||
di.override(fetchInjectable, () => fetchMock);
|
di.override(fetchInjectable, () => fetchMock);
|
||||||
|
|
||||||
@ -184,7 +161,9 @@ describe("KubeApi", () => {
|
|||||||
apiBase: "/api-kube",
|
apiBase: "/api-kube",
|
||||||
});
|
});
|
||||||
|
|
||||||
di.inject(autoRegistrationInjectable);
|
const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
|
||||||
|
|
||||||
|
setupAutoRegistration.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("patching deployments", () => {
|
describe("patching deployments", () => {
|
||||||
|
|||||||
@ -3,27 +3,22 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Cluster } from "../../cluster/cluster";
|
|
||||||
import type { ClusterContext } from "../cluster-context";
|
|
||||||
import type { KubeApi } from "../kube-api";
|
import type { KubeApi } from "../kube-api";
|
||||||
import { KubeObject } from "../kube-object";
|
import { KubeObject } from "../kube-object";
|
||||||
import type { KubeObjectStoreLoadingParams } from "../kube-object.store";
|
import type { KubeObjectStoreLoadingParams } from "../kube-object.store";
|
||||||
import { KubeObjectStore } from "../kube-object.store";
|
import { KubeObjectStore } from "../kube-object.store";
|
||||||
|
|
||||||
class FakeKubeObjectStore extends KubeObjectStore<KubeObject> {
|
class FakeKubeObjectStore extends KubeObjectStore<KubeObject> {
|
||||||
_context = {
|
constructor(private readonly _loadItems: (params: KubeObjectStoreLoadingParams) => KubeObject[], api: Partial<KubeApi<KubeObject>>) {
|
||||||
|
super({
|
||||||
|
context: {
|
||||||
allNamespaces: [],
|
allNamespaces: [],
|
||||||
contextNamespaces: [],
|
contextNamespaces: [],
|
||||||
hasSelectedAll: false,
|
hasSelectedAll: false,
|
||||||
cluster: {} as Cluster,
|
isGlobalWatchEnabled: () => true,
|
||||||
} as ClusterContext;
|
isLoadingAll: () => true,
|
||||||
|
},
|
||||||
get context() {
|
}, api as KubeApi<KubeObject>);
|
||||||
return this._context;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private readonly _loadItems: (params: KubeObjectStoreLoadingParams) => KubeObject[], api: Partial<KubeApi<KubeObject>>) {
|
|
||||||
super(api as KubeApi<KubeObject>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadItems(params: KubeObjectStoreLoadingParams) {
|
async loadItems(params: KubeObjectStoreLoadingParams) {
|
||||||
|
|||||||
@ -1,74 +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 { CustomResourceDefinition } from "../endpoints";
|
|
||||||
import { KubeApi } from "../kube-api";
|
|
||||||
import { KubeObject } from "../kube-object";
|
|
||||||
import autoRegistrationEmitterInjectable from "./auto-registration-emitter.injectable";
|
|
||||||
import apiManagerInjectable from "./manager.injectable";
|
|
||||||
import { CustomResourceStore } from "./resource.store";
|
|
||||||
|
|
||||||
const autoRegistrationInjectable = getInjectable({
|
|
||||||
id: "api-manager-auto-registration",
|
|
||||||
instantiate: (di) => {
|
|
||||||
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
|
|
||||||
const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
|
|
||||||
const beforeApiManagerInitializationApis: KubeApi[] = [];
|
|
||||||
let initialized = false;
|
|
||||||
|
|
||||||
const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
|
|
||||||
const objectConstructor = class extends KubeObject {
|
|
||||||
static readonly kind = crd.getResourceKind();
|
|
||||||
static readonly namespaced = crd.isNamespaced();
|
|
||||||
static readonly apiBase = crd.getResourceApiBase();
|
|
||||||
};
|
|
||||||
|
|
||||||
const api = (() => {
|
|
||||||
const rawApi = apiManager.getApi(objectConstructor.apiBase);
|
|
||||||
|
|
||||||
if (rawApi) {
|
|
||||||
return rawApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
const api = new KubeApi({ objectConstructor });
|
|
||||||
|
|
||||||
apiManager.registerApi(api);
|
|
||||||
|
|
||||||
return api;
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (!apiManager.getStore(api)) {
|
|
||||||
apiManager.registerStore(new CustomResourceStore(api));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const autoInitKubeApi = (api: KubeApi) => {
|
|
||||||
apiManager.registerApi(api);
|
|
||||||
};
|
|
||||||
|
|
||||||
autoRegistrationEmitter
|
|
||||||
.on("customResourceDefinition", (crd) => {
|
|
||||||
if (initialized) {
|
|
||||||
autoInitCustomResourceStore(crd);
|
|
||||||
} else {
|
|
||||||
beforeApiManagerInitializationCrds.push(crd);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on("kubeApi", (api) => {
|
|
||||||
if (initialized) {
|
|
||||||
autoInitKubeApi(api);
|
|
||||||
} else {
|
|
||||||
beforeApiManagerInitializationApis.push(api);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const apiManager = di.inject(apiManagerInjectable);
|
|
||||||
|
|
||||||
beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
|
|
||||||
beforeApiManagerInitializationApis.forEach(autoInitKubeApi);
|
|
||||||
initialized = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default autoRegistrationInjectable;
|
|
||||||
@ -4,11 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { KubeApi } from "../kube-api";
|
import type { KubeApi } from "../kube-api";
|
||||||
|
import type { KubeObjectStoreDependencies } from "../kube-object.store";
|
||||||
import { KubeObjectStore } from "../kube-object.store";
|
import { KubeObjectStore } from "../kube-object.store";
|
||||||
import type { KubeObject } from "../kube-object";
|
import type { KubeObject } from "../kube-object";
|
||||||
|
|
||||||
export class CustomResourceStore<K extends KubeObject> extends KubeObjectStore<K, KubeApi<K>> {
|
export class CustomResourceStore<K extends KubeObject> extends KubeObjectStore<K, KubeApi<K>> {
|
||||||
constructor(api: KubeApi<K>) {
|
constructor(deps: KubeObjectStoreDependencies, api: KubeApi<K>) {
|
||||||
super(api);
|
super(deps, api);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { Cluster } from "../cluster/cluster";
|
|
||||||
|
|
||||||
export interface ClusterContext {
|
|
||||||
cluster: Cluster;
|
|
||||||
allNamespaces: string[]; // available / allowed namespaces from cluster.ts
|
|
||||||
contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
|
|
||||||
hasSelectedAll: boolean;
|
|
||||||
}
|
|
||||||
@ -5,25 +5,44 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { Patch } from "rfc6902";
|
import type { Patch } from "rfc6902";
|
||||||
import apiBaseInjectable from "../../api-base.injectable";
|
import apiBaseInjectable from "../../api-base.injectable";
|
||||||
|
import type { AsyncResult } from "../../../utils/async-result";
|
||||||
import type { KubeJsonApiData } from "../../kube-json-api";
|
import type { KubeJsonApiData } from "../../kube-json-api";
|
||||||
|
|
||||||
export type RequestKubeObjectPatch = (name: string, kind: string, ns: string | undefined, patch: Patch) => Promise<KubeJsonApiData>;
|
export type RequestKubeObjectPatch = (name: string, kind: string, ns: string | undefined, patch: Patch) => Promise<AsyncResult<KubeJsonApiData, string>>;
|
||||||
|
|
||||||
const requestKubeObjectPatchInjectable = getInjectable({
|
const requestKubeObjectPatchInjectable = getInjectable({
|
||||||
id: "request-kube-object-patch",
|
id: "request-kube-object-patch",
|
||||||
instantiate: (di): RequestKubeObjectPatch => {
|
instantiate: (di): RequestKubeObjectPatch => {
|
||||||
const apiBase = di.inject(apiBaseInjectable);
|
const apiBase = di.inject(apiBaseInjectable);
|
||||||
|
|
||||||
return (name, kind, ns, patch) => (
|
return async (name, kind, ns, patch) => {
|
||||||
apiBase.patch("/stack", {
|
const result = await apiBase.patch("/stack", {
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
kind,
|
kind,
|
||||||
ns,
|
ns,
|
||||||
patch,
|
patch,
|
||||||
},
|
},
|
||||||
})
|
}) as AsyncResult<string, string>;
|
||||||
);
|
|
||||||
|
if (!result.callWasSuccessful) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = JSON.parse(result.response);
|
||||||
|
|
||||||
|
return {
|
||||||
|
callWasSuccessful: true,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
callWasSuccessful: false,
|
||||||
|
error: String(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,37 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import apiBaseInjectable from "../../api-base.injectable";
|
import apiBaseInjectable from "../../api-base.injectable";
|
||||||
|
import type { AsyncResult } from "../../../utils/async-result";
|
||||||
import type { KubeJsonApiData } from "../../kube-json-api";
|
import type { KubeJsonApiData } from "../../kube-json-api";
|
||||||
|
|
||||||
export type RequestKubeObjectCreation = (resourceDescriptor: string) => Promise<KubeJsonApiData>;
|
export type RequestKubeObjectCreation = (resourceDescriptor: string) => Promise<AsyncResult<KubeJsonApiData, string>>;
|
||||||
|
|
||||||
const requestKubeObjectCreationInjectable = getInjectable({
|
const requestKubeObjectCreationInjectable = getInjectable({
|
||||||
id: "request-kube-object-creation",
|
id: "request-kube-object-creation",
|
||||||
instantiate: (di): RequestKubeObjectCreation => {
|
instantiate: (di): RequestKubeObjectCreation => {
|
||||||
const apiBase = di.inject(apiBaseInjectable);
|
const apiBase = di.inject(apiBaseInjectable);
|
||||||
|
|
||||||
return (data) => apiBase.post("/stack", { data });
|
return async (data) => {
|
||||||
|
const result = await apiBase.post("/stack", { data }) as AsyncResult<string, string>;
|
||||||
|
|
||||||
|
if (!result.callWasSuccessful) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = JSON.parse(result.response);
|
||||||
|
|
||||||
|
return {
|
||||||
|
callWasSuccessful: true,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
callWasSuccessful: false,
|
||||||
|
error: String(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -104,7 +104,7 @@ export class JsonApi<Data = JsonApiData, Params extends JsonApiParams<Data> = Js
|
|||||||
);
|
);
|
||||||
const { query } = params ?? {};
|
const { query } = params ?? {};
|
||||||
|
|
||||||
if (query) {
|
if (query && Object.keys(query).length > 0) {
|
||||||
const queryString = stringify(query as unknown as QueryParams);
|
const queryString = stringify(query as unknown as QueryParams);
|
||||||
|
|
||||||
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
|
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
|
||||||
@ -171,7 +171,7 @@ export class JsonApi<Data = JsonApiData, Params extends JsonApiParams<Data> = Js
|
|||||||
reqInit.body = JSON.stringify(data);
|
reqInit.body = JSON.stringify(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query) {
|
if (query && Object.keys(query).length > 0) {
|
||||||
const queryString = stringify(query as unknown as QueryParams);
|
const queryString = stringify(query as unknown as QueryParams);
|
||||||
|
|
||||||
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
|
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
|
||||||
|
|||||||
@ -403,10 +403,11 @@ export class KubeApi<
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This method differs from {@link formatUrlForNotListing} because this treats `""` as "all namespaces"
|
* This method differs from {@link formatUrlForNotListing} because this treats `""` as "all namespaces"
|
||||||
|
* NOTE: This is also useful for watching
|
||||||
* @param namespace The namespace to list in or `""` for all namespaces
|
* @param namespace The namespace to list in or `""` for all namespaces
|
||||||
*/
|
*/
|
||||||
formatUrlForListing(namespace: string) {
|
formatUrlForListing(namespace: string | undefined, query?: Partial<KubeApiQueryParams>) {
|
||||||
return createKubeApiURL({
|
const resourcePath = createKubeApiURL({
|
||||||
apiPrefix: this.apiPrefix,
|
apiPrefix: this.apiPrefix,
|
||||||
apiVersion: this.apiVersionWithGroup,
|
apiVersion: this.apiVersionWithGroup,
|
||||||
resource: this.apiResource,
|
resource: this.apiResource,
|
||||||
@ -414,15 +415,15 @@ export class KubeApi<
|
|||||||
? namespace ?? "default"
|
? namespace ?? "default"
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a URL pathname and query for acting upon a specific resource.
|
* Format a URL pathname and query for acting upon a specific resource.
|
||||||
*/
|
*/
|
||||||
formatUrlForNotListing(resource?: Partial<ResourceDescriptor>, query?: Partial<KubeApiQueryParams>): string;
|
formatUrlForNotListing({ name, namespace }: Partial<ResourceDescriptor> = {}) {
|
||||||
|
return createKubeApiURL({
|
||||||
formatUrlForNotListing({ name, namespace }: Partial<ResourceDescriptor> = {}, query?: Partial<KubeApiQueryParams>) {
|
|
||||||
const resourcePath = createKubeApiURL({
|
|
||||||
apiPrefix: this.apiPrefix,
|
apiPrefix: this.apiPrefix,
|
||||||
apiVersion: this.apiVersionWithGroup,
|
apiVersion: this.apiVersionWithGroup,
|
||||||
resource: this.apiResource,
|
resource: this.apiResource,
|
||||||
@ -431,15 +432,17 @@ export class KubeApi<
|
|||||||
: undefined,
|
: undefined,
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
|
|
||||||
return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use {@link formatUrlForNotListing} instead
|
* @deprecated use {@link formatUrlForNotListing} or {@link formatUrlForListing} instead
|
||||||
*/
|
*/
|
||||||
getUrl(resource?: Partial<ResourceDescriptor>, query?: Partial<KubeApiQueryParams>) {
|
getUrl(resource?: Partial<ResourceDescriptor>, query?: Partial<KubeApiQueryParams>) {
|
||||||
return this.formatUrlForNotListing(resource, query);
|
if (query) {
|
||||||
|
return this.formatUrlForListing(resource?.namespace, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formatUrlForNotListing(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected normalizeQuery(query: Partial<KubeApiQueryParams> = {}) {
|
protected normalizeQuery(query: Partial<KubeApiQueryParams> = {}) {
|
||||||
@ -625,14 +628,14 @@ export class KubeApi<
|
|||||||
}
|
}
|
||||||
|
|
||||||
getWatchUrl(namespace?: string, query: KubeApiQueryParams = {}) {
|
getWatchUrl(namespace?: string, query: KubeApiQueryParams = {}) {
|
||||||
return this.formatUrlForNotListing({ namespace }, {
|
return this.formatUrlForListing(namespace, {
|
||||||
watch: 1,
|
watch: 1,
|
||||||
resourceVersion: this.getResourceVersion(namespace),
|
resourceVersion: this.getResourceVersion(namespace),
|
||||||
...query,
|
...query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(opts?: KubeApiWatchOptions<Object, Data>): () => void {
|
watch(opts?: KubeApiWatchOptions<Object, Data>): Disposer {
|
||||||
let errorReceived = false;
|
let errorReceived = false;
|
||||||
let timedRetry: NodeJS.Timeout;
|
let timedRetry: NodeJS.Timeout;
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -3,11 +3,9 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ClusterContext } from "./cluster-context";
|
import { action, computed, makeObservable, observable, reaction } from "mobx";
|
||||||
|
|
||||||
import { action, computed, makeObservable, observable, reaction, when } from "mobx";
|
|
||||||
import type { Disposer } from "../utils";
|
import type { Disposer } from "../utils";
|
||||||
import { waitUntilDefined, autoBind, includes, noop, rejectPromiseBy } from "../utils";
|
import { waitUntilDefined, autoBind, includes, rejectPromiseBy } from "../utils";
|
||||||
import type { KubeJsonApiDataFor, KubeObject } from "./kube-object";
|
import type { KubeJsonApiDataFor, KubeObject } from "./kube-object";
|
||||||
import { KubeStatus } from "./kube-object";
|
import { KubeStatus } from "./kube-object";
|
||||||
import type { IKubeWatchEvent } from "./kube-watch-event";
|
import type { IKubeWatchEvent } from "./kube-watch-event";
|
||||||
@ -21,6 +19,7 @@ import assert from "assert";
|
|||||||
import type { PartialDeep } from "type-fest";
|
import type { PartialDeep } from "type-fest";
|
||||||
import { entries } from "../utils/objects";
|
import { entries } from "../utils/objects";
|
||||||
import AbortController from "abort-controller";
|
import AbortController from "abort-controller";
|
||||||
|
import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context";
|
||||||
|
|
||||||
export type OnLoadFailure = (error: unknown) => void;
|
export type OnLoadFailure = (error: unknown) => void;
|
||||||
|
|
||||||
@ -85,38 +84,26 @@ export type KubeApiDataFrom<K extends KubeObject, A> = A extends KubeApi<K, infe
|
|||||||
|
|
||||||
export type JsonPatch = Patch;
|
export type JsonPatch = Patch;
|
||||||
|
|
||||||
|
export interface KubeObjectStoreDependencies {
|
||||||
|
readonly context: ClusterContext;
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class KubeObjectStore<
|
export abstract class KubeObjectStore<
|
||||||
K extends KubeObject = KubeObject,
|
K extends KubeObject = KubeObject,
|
||||||
A extends KubeApi<K, D> = KubeApi<K, KubeJsonApiDataFor<K>>,
|
A extends KubeApi<K, D> = KubeApi<K, KubeJsonApiDataFor<K>>,
|
||||||
D extends KubeJsonApiDataFor<K> = KubeApiDataFrom<K, A>,
|
D extends KubeJsonApiDataFor<K> = KubeApiDataFrom<K, A>,
|
||||||
> extends ItemStore<K> {
|
> extends ItemStore<K> {
|
||||||
static readonly defaultContext = observable.box<ClusterContext>(); // TODO: support multiple cluster contexts
|
|
||||||
|
|
||||||
public readonly api!: A;
|
|
||||||
public readonly limit: number | undefined;
|
public readonly limit: number | undefined;
|
||||||
public readonly bufferSize: number;
|
public readonly bufferSize: number;
|
||||||
@observable private loadedNamespaces: string[] | undefined = undefined;
|
|
||||||
|
|
||||||
get contextReady() {
|
private readonly loadedNamespaces = observable.box<string[]>();
|
||||||
return when(() => Boolean(this.context));
|
|
||||||
}
|
|
||||||
|
|
||||||
get namespacesReady() {
|
constructor(
|
||||||
return when(() => Boolean(this.loadedNamespaces));
|
protected readonly dependencies: KubeObjectStoreDependencies,
|
||||||
}
|
public readonly api: A,
|
||||||
|
opts?: KubeObjectStoreOptions,
|
||||||
constructor(api: A, opts?: KubeObjectStoreOptions);
|
) {
|
||||||
/**
|
|
||||||
* @deprecated Supply API instance through constructor
|
|
||||||
*/
|
|
||||||
constructor();
|
|
||||||
constructor(api?: A, opts?: KubeObjectStoreOptions) {
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (api) {
|
|
||||||
this.api = api;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.limit = opts?.limit;
|
this.limit = opts?.limit;
|
||||||
this.bufferSize = opts?.bufferSize ?? 50_000;
|
this.bufferSize = opts?.bufferSize ?? 50_000;
|
||||||
|
|
||||||
@ -125,13 +112,9 @@ export abstract class KubeObjectStore<
|
|||||||
this.bindWatchEventsUpdater();
|
this.bindWatchEventsUpdater();
|
||||||
}
|
}
|
||||||
|
|
||||||
get context(): ClusterContext | undefined {
|
|
||||||
return KubeObjectStore.defaultContext.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Circular dependency: KubeObjectStore -> ClusterFrameContext -> NamespaceStore -> KubeObjectStore
|
// TODO: Circular dependency: KubeObjectStore -> ClusterFrameContext -> NamespaceStore -> KubeObjectStore
|
||||||
@computed get contextItems(): K[] {
|
@computed get contextItems(): K[] {
|
||||||
const namespaces = this.context?.contextNamespaces ?? [];
|
const namespaces = this.dependencies.context.contextNamespaces;
|
||||||
|
|
||||||
return this.items.filter(item => {
|
return this.items.filter(item => {
|
||||||
const itemNamespace = item.getNs();
|
const itemNamespace = item.getNs();
|
||||||
@ -202,17 +185,11 @@ export abstract class KubeObjectStore<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async loadItems({ namespaces, reqInit, onLoadFailure }: KubeObjectStoreLoadingParams): Promise<K[]> {
|
protected async loadItems({ namespaces, reqInit, onLoadFailure }: KubeObjectStoreLoadingParams): Promise<K[]> {
|
||||||
if (!this.context?.cluster?.isAllowedResource(this.api.kind)) {
|
const isLoadingAll = this.dependencies.context.isLoadingAll(namespaces);
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLoadingAll = this.context.allNamespaces?.length > 1
|
|
||||||
&& this.context.cluster.accessibleNamespaces.length === 0
|
|
||||||
&& this.context.allNamespaces.every(ns => namespaces.includes(ns));
|
|
||||||
|
|
||||||
if (!this.api.isNamespaced || isLoadingAll) {
|
if (!this.api.isNamespaced || isLoadingAll) {
|
||||||
if (this.api.isNamespaced) {
|
if (this.api.isNamespaced) {
|
||||||
this.loadedNamespaces = [];
|
this.loadedNamespaces.set([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = this.api.list({ reqInit }, this.query);
|
const res = this.api.list({ reqInit }, this.query);
|
||||||
@ -234,7 +211,7 @@ export abstract class KubeObjectStore<
|
|||||||
return await res ?? [];
|
return await res ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadedNamespaces = namespaces;
|
this.loadedNamespaces.set(namespaces);
|
||||||
|
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
namespaces.map(namespace => this.api.list({ namespace, reqInit }, this.query)),
|
namespaces.map(namespace => this.api.list({ namespace, reqInit }, this.query)),
|
||||||
@ -266,9 +243,7 @@ export abstract class KubeObjectStore<
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
async loadAll({ namespaces, merge = true, reqInit, onLoadFailure }: KubeObjectStoreLoadAllParams = {}): Promise<undefined | K[]> {
|
async loadAll({ namespaces, merge = true, reqInit, onLoadFailure }: KubeObjectStoreLoadAllParams = {}): Promise<undefined | K[]> {
|
||||||
const context = await waitUntilDefined(() => this.context);
|
namespaces ??= this.dependencies.context.contextNamespaces;
|
||||||
|
|
||||||
namespaces ??= context.contextNamespaces;
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -425,7 +400,7 @@ export abstract class KubeObjectStore<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// collect items from watch-api events to avoid UI blowing up with huge streams of data
|
// collect items from watch-api events to avoid UI blowing up with huge streams of data
|
||||||
protected eventsBuffer = observable.array<IKubeWatchEvent<D>>([], { deep: false });
|
protected readonly eventsBuffer = observable.array<IKubeWatchEvent<D>>([], { deep: false });
|
||||||
|
|
||||||
protected bindWatchEventsUpdater(delay = 1000) {
|
protected bindWatchEventsUpdater(delay = 1000) {
|
||||||
reaction(() => [...this.eventsBuffer], this.updateFromEventsBuffer, {
|
reaction(() => [...this.eventsBuffer], this.updateFromEventsBuffer, {
|
||||||
@ -435,25 +410,24 @@ export abstract class KubeObjectStore<
|
|||||||
|
|
||||||
subscribe({ onLoadFailure, abortController = new AbortController() }: KubeObjectStoreSubscribeParams = {}): Disposer {
|
subscribe({ onLoadFailure, abortController = new AbortController() }: KubeObjectStoreSubscribeParams = {}): Disposer {
|
||||||
if (this.api.isNamespaced) {
|
if (this.api.isNamespaced) {
|
||||||
Promise.race([
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const loadedNamespaces = await Promise.race([
|
||||||
rejectPromiseBy(abortController.signal),
|
rejectPromiseBy(abortController.signal),
|
||||||
Promise.all([
|
waitUntilDefined(() => this.loadedNamespaces.get()),
|
||||||
waitUntilDefined(() => this.context),
|
]);
|
||||||
this.namespacesReady,
|
|
||||||
] as const),
|
|
||||||
])
|
|
||||||
.then(([context]) => {
|
|
||||||
assert(this.loadedNamespaces);
|
|
||||||
|
|
||||||
if (context.cluster?.isGlobalWatchEnabled && this.loadedNamespaces.length === 0) {
|
if (this.dependencies.context.isGlobalWatchEnabled() && loadedNamespaces.length === 0) {
|
||||||
return this.watchNamespace("", abortController, { onLoadFailure });
|
this.watchNamespace("", abortController, { onLoadFailure });
|
||||||
}
|
} else {
|
||||||
|
for (const namespace of loadedNamespaces) {
|
||||||
for (const namespace of this.loadedNamespaces) {
|
|
||||||
this.watchNamespace(namespace, abortController, { onLoadFailure });
|
this.watchNamespace(namespace, abortController, { onLoadFailure });
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.catch(noop); // ignore DOMExceptions
|
} catch (error) {
|
||||||
|
console.error(`[KUBE-OBJECT-STORE]: failed to subscribe to ${this.api.apiBase}`, error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
} else {
|
} else {
|
||||||
this.watchNamespace("", abortController, { onLoadFailure });
|
this.watchNamespace("", abortController, { onLoadFailure });
|
||||||
}
|
}
|
||||||
@ -467,7 +441,7 @@ export abstract class KubeObjectStore<
|
|||||||
}
|
}
|
||||||
|
|
||||||
let timedRetry: NodeJS.Timeout;
|
let timedRetry: NodeJS.Timeout;
|
||||||
const watch = () => this.api.watch({
|
const startNewWatch = () => this.api.watch({
|
||||||
namespace,
|
namespace,
|
||||||
abortController,
|
abortController,
|
||||||
callback,
|
callback,
|
||||||
@ -486,7 +460,7 @@ export abstract class KubeObjectStore<
|
|||||||
|
|
||||||
// not sure what to do, best to retry
|
// not sure what to do, best to retry
|
||||||
clearTimeout(timedRetry);
|
clearTimeout(timedRetry);
|
||||||
timedRetry = setTimeout(watch, 5000);
|
timedRetry = setTimeout(startNewWatch, 5000);
|
||||||
} else if (error instanceof KubeStatus && error.code === 410) {
|
} else if (error instanceof KubeStatus && error.code === 410) {
|
||||||
clearTimeout(timedRetry);
|
clearTimeout(timedRetry);
|
||||||
// resourceVersion has gone, let's try to reload
|
// resourceVersion has gone, let's try to reload
|
||||||
@ -495,11 +469,11 @@ export abstract class KubeObjectStore<
|
|||||||
namespace
|
namespace
|
||||||
? this.loadAll({ namespaces: [namespace], reqInit: { signal }, ...opts })
|
? this.loadAll({ namespaces: [namespace], reqInit: { signal }, ...opts })
|
||||||
: this.loadAll({ merge: false, reqInit: { signal }, ...opts })
|
: this.loadAll({ merge: false, reqInit: { signal }, ...opts })
|
||||||
).then(watch);
|
).then(startNewWatch);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else if (error) { // not sure what to do, best to retry
|
} else if (error) { // not sure what to do, best to retry
|
||||||
clearTimeout(timedRetry);
|
clearTimeout(timedRetry);
|
||||||
timedRetry = setTimeout(watch, 5000);
|
timedRetry = setTimeout(startNewWatch, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -508,7 +482,7 @@ export abstract class KubeObjectStore<
|
|||||||
};
|
};
|
||||||
|
|
||||||
signal.addEventListener("abort", () => clearTimeout(timedRetry));
|
signal.addEventListener("abort", () => clearTimeout(timedRetry));
|
||||||
watch();
|
startNewWatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@ -645,8 +645,13 @@ export class KubeObject<
|
|||||||
}
|
}
|
||||||
|
|
||||||
const requestKubeObjectPatch = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectPatchInjectable);
|
const requestKubeObjectPatch = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectPatchInjectable);
|
||||||
|
const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
|
||||||
|
|
||||||
return requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
|
if (!result.callWasSuccessful) {
|
||||||
|
throw new Error(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -665,7 +670,13 @@ export class KubeObject<
|
|||||||
...data,
|
...data,
|
||||||
});
|
});
|
||||||
|
|
||||||
return requestKubeObjectCreation(descriptor);
|
const result = await requestKubeObjectCreation(descriptor);
|
||||||
|
|
||||||
|
if (!result.callWasSuccessful) {
|
||||||
|
throw new Error(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -9,13 +9,23 @@ import { loggerTransportInjectionToken } from "./logger/transports";
|
|||||||
|
|
||||||
const loggerInjectable = getInjectable({
|
const loggerInjectable = getInjectable({
|
||||||
id: "logger",
|
id: "logger",
|
||||||
instantiate: (di): Logger => createLogger({
|
instantiate: (di): Logger => {
|
||||||
|
const baseLogger = createLogger({
|
||||||
format: format.combine(
|
format: format.combine(
|
||||||
format.splat(),
|
format.splat(),
|
||||||
format.simple(),
|
format.simple(),
|
||||||
),
|
),
|
||||||
transports: di.injectMany(loggerTransportInjectionToken),
|
transports: di.injectMany(loggerTransportInjectionToken),
|
||||||
}),
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
debug: (message, ...data) => baseLogger.debug(message, ...data),
|
||||||
|
info: (message, ...data) => baseLogger.info(message, ...data),
|
||||||
|
warn: (message, ...data) => baseLogger.warn(message, ...data),
|
||||||
|
error: (message, ...data) => baseLogger.error(message, ...data),
|
||||||
|
silly: (message, ...data) => baseLogger.silly(message, ...data),
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default loggerInjectable;
|
export default loggerInjectable;
|
||||||
|
|||||||
@ -17,4 +17,6 @@ export interface Logger {
|
|||||||
/**
|
/**
|
||||||
* @deprecated use `di.inject(loggerInjectable)` instead
|
* @deprecated use `di.inject(loggerInjectable)` instead
|
||||||
*/
|
*/
|
||||||
export default asLegacyGlobalForExtensionApi(loggerInjectable);
|
const logger = asLegacyGlobalForExtensionApi(loggerInjectable);
|
||||||
|
|
||||||
|
export default logger;
|
||||||
|
|||||||
@ -11,51 +11,190 @@ export type KubeResource =
|
|||||||
"priorityclasses" | "runtimeclasses" |
|
"priorityclasses" | "runtimeclasses" |
|
||||||
"roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts";
|
"roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts";
|
||||||
|
|
||||||
export interface KubeApiResource extends KubeApiResourceData {
|
export interface KubeApiResource {
|
||||||
apiName: KubeResource; // valid api resource name (e.g. "namespaces")
|
kind: string;
|
||||||
|
group: string;
|
||||||
|
apiName: string;
|
||||||
|
namespaced: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface KubeApiResourceDescriptor {
|
||||||
|
apiName: string;
|
||||||
|
group: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatKubeApiResource = (res: KubeApiResourceDescriptor) => `${res.group}/${res.apiName}`;
|
||||||
|
|
||||||
export interface KubeApiResourceData {
|
export interface KubeApiResourceData {
|
||||||
kind: string; // resource type (e.g. "Namespace")
|
kind: string; // resource type (e.g. "Namespace")
|
||||||
group?: string; // api-group
|
group: string; // api-group, if empty then "core"
|
||||||
|
namespaced: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
|
export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
|
||||||
"clusterroles": { kind: "ClusterRole", group: "rbac.authorization.k8s.io" },
|
clusterroles: {
|
||||||
"clusterrolebindings": { kind: "ClusterRoleBinding", group: "rbac.authorization.k8s.io" },
|
kind: "ClusterRole",
|
||||||
"configmaps": { kind: "ConfigMap" }, //empty group means "core"
|
group: "rbac.authorization.k8s.io",
|
||||||
"cronjobs": { kind: "CronJob", group: "batch" },
|
namespaced: false,
|
||||||
"customresourcedefinitions": { kind: "CustomResourceDefinition", group: "apiextensions.k8s.io" },
|
},
|
||||||
"daemonsets": { kind: "DaemonSet", group: "apps" },
|
clusterrolebindings: {
|
||||||
"deployments": { kind: "Deployment", group: "apps" },
|
kind: "ClusterRoleBinding",
|
||||||
"endpoints": { kind: "Endpoint" },
|
group: "rbac.authorization.k8s.io",
|
||||||
"events": { kind: "Event" },
|
namespaced: false,
|
||||||
"horizontalpodautoscalers": { kind: "HorizontalPodAutoscaler", group: "autoscaling" },
|
},
|
||||||
"ingresses": { kind: "Ingress", group: "networking.k8s.io" },
|
configmaps: {
|
||||||
"jobs": { kind: "Job", group: "batch" },
|
kind: "ConfigMap",
|
||||||
"namespaces": { kind: "Namespace" },
|
group: "v1",
|
||||||
"limitranges": { kind: "LimitRange" },
|
namespaced: true,
|
||||||
"leases": { kind: "Lease" },
|
},
|
||||||
"networkpolicies": { kind: "NetworkPolicy", group: "networking.k8s.io" },
|
cronjobs: {
|
||||||
"nodes": { kind: "Node" },
|
kind: "CronJob",
|
||||||
"persistentvolumes": { kind: "PersistentVolume" },
|
group: "batch",
|
||||||
"persistentvolumeclaims": { kind: "PersistentVolumeClaim" },
|
namespaced: true,
|
||||||
"pods": { kind: "Pod" },
|
},
|
||||||
"poddisruptionbudgets": { kind: "PodDisruptionBudget", group: "policy" },
|
customresourcedefinitions: {
|
||||||
"podsecuritypolicies": { kind: "PodSecurityPolicy", group: "policy" },
|
kind: "CustomResourceDefinition",
|
||||||
"priorityclasses": { kind: "PriorityClass", group: "scheduling.k8s.io" },
|
group: "apiextensions.k8s.io",
|
||||||
"runtimeclasses": { kind: "RuntimeClass", group: "node.k8s.io" },
|
namespaced: false,
|
||||||
"resourcequotas": { kind: "ResourceQuota" },
|
},
|
||||||
"replicasets": { kind: "ReplicaSet", group: "apps" },
|
daemonsets: {
|
||||||
"roles": { kind: "Role", group: "rbac.authorization.k8s.io" },
|
kind: "DaemonSet",
|
||||||
"rolebindings": { kind: "RoleBinding", group: "rbac.authorization.k8s.io" },
|
group: "apps",
|
||||||
"secrets": { kind: "Secret" },
|
namespaced: true,
|
||||||
"serviceaccounts": { kind: "ServiceAccount" },
|
},
|
||||||
"services": { kind: "Service" },
|
deployments: {
|
||||||
"statefulsets": { kind: "StatefulSet", group: "apps" },
|
kind: "Deployment",
|
||||||
"storageclasses": { kind: "StorageClass", group: "storage.k8s.io" },
|
group: "apps",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
endpoints: {
|
||||||
|
kind: "Endpoint",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
kind: "Event",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
horizontalpodautoscalers: {
|
||||||
|
kind: "HorizontalPodAutoscaler",
|
||||||
|
group: "autoscaling",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
ingresses: {
|
||||||
|
kind: "Ingress",
|
||||||
|
group: "networking.k8s.io",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
jobs: {
|
||||||
|
kind: "Job",
|
||||||
|
group: "batch",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
namespaces: {
|
||||||
|
kind: "Namespace",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
|
limitranges: {
|
||||||
|
kind: "LimitRange",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
leases: {
|
||||||
|
kind: "Lease",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
networkpolicies: {
|
||||||
|
kind: "NetworkPolicy",
|
||||||
|
group: "networking.k8s.io",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
nodes: {
|
||||||
|
kind: "Node",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
|
persistentvolumes: {
|
||||||
|
kind: "PersistentVolume",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
|
persistentvolumeclaims: {
|
||||||
|
kind: "PersistentVolumeClaim",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
pods: {
|
||||||
|
kind: "Pod",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
poddisruptionbudgets: {
|
||||||
|
kind: "PodDisruptionBudget",
|
||||||
|
group: "policy",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
podsecuritypolicies: {
|
||||||
|
kind: "PodSecurityPolicy",
|
||||||
|
group: "policy",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
|
priorityclasses: {
|
||||||
|
kind: "PriorityClass",
|
||||||
|
group: "scheduling.k8s.io",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
|
runtimeclasses: {
|
||||||
|
kind: "RuntimeClass",
|
||||||
|
group: "node.k8s.io",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
|
resourcequotas: {
|
||||||
|
kind: "ResourceQuota",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
replicasets: {
|
||||||
|
kind: "ReplicaSet",
|
||||||
|
group: "apps",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
kind: "Role",
|
||||||
|
group: "rbac.authorization.k8s.io",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
rolebindings: {
|
||||||
|
kind: "RoleBinding",
|
||||||
|
group: "rbac.authorization.k8s.io",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
secrets: {
|
||||||
|
kind: "Secret",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
serviceaccounts: {
|
||||||
|
kind: "ServiceAccount",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
kind: "Service",
|
||||||
|
group: "v1",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
statefulsets: {
|
||||||
|
kind: "StatefulSet",
|
||||||
|
group: "apps",
|
||||||
|
namespaced: true,
|
||||||
|
},
|
||||||
|
storageclasses: {
|
||||||
|
kind: "StorageClass",
|
||||||
|
group: "storage.k8s.io",
|
||||||
|
namespaced: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7)
|
|
||||||
export const apiResources: KubeApiResource[] = Object.entries(apiResourceRecord)
|
|
||||||
.map(([apiName, data]) => ({ apiName: apiName as KubeResource, ...data }));
|
|
||||||
|
|||||||
11
src/common/utils/computed-or.ts
Normal file
11
src/common/utils/computed-or.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
|
import { computed } from "mobx";
|
||||||
|
|
||||||
|
export const computedOr = (...values: IComputedValue<boolean>[]) => computed((
|
||||||
|
() => values.some(value => value.get())
|
||||||
|
));
|
||||||
@ -1,26 +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 { computed } from "mobx";
|
|
||||||
import type { KubeResource } from "../rbac";
|
|
||||||
import { allowedResourcesInjectionToken } from "../cluster-store/allowed-resources-injection-token";
|
|
||||||
|
|
||||||
export type IsAllowedResource = (resource: KubeResource) => boolean;
|
|
||||||
|
|
||||||
const isAllowedResourceInjectable = getInjectable({
|
|
||||||
id: "is-allowed-resource",
|
|
||||||
|
|
||||||
instantiate: (di, resourceName: string) => {
|
|
||||||
const allowedResources = di.inject(allowedResourcesInjectionToken);
|
|
||||||
|
|
||||||
return computed(() => allowedResources.get().has(resourceName));
|
|
||||||
},
|
|
||||||
|
|
||||||
lifecycle: lifecycleEnum.keyedSingleton({
|
|
||||||
getInstanceKey: (di, resource: string) => resource,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default isAllowedResourceInjectable;
|
|
||||||
@ -8,26 +8,23 @@ import type { Disposer } from "./disposer";
|
|||||||
|
|
||||||
export async function waitUntilDefined<T>(getter: (() => T | null | undefined) | IComputedValue<T | null | undefined>, opts?: { timeout?: number }): Promise<T> {
|
export async function waitUntilDefined<T>(getter: (() => T | null | undefined) | IComputedValue<T | null | undefined>, opts?: { timeout?: number }): Promise<T> {
|
||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
let res: T | null | undefined;
|
|
||||||
|
|
||||||
when(
|
when(
|
||||||
() => {
|
() => {
|
||||||
res = typeof getter === "function"
|
const res = typeof getter === "function"
|
||||||
? getter()
|
? getter()
|
||||||
: getter.get();
|
: getter.get();
|
||||||
|
const isDefined = res != null;
|
||||||
|
|
||||||
if (res != null) {
|
if (isDefined) {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return isDefined;
|
||||||
},
|
},
|
||||||
() => {},
|
() => {},
|
||||||
{
|
{
|
||||||
onError: reject,
|
onError: reject,
|
||||||
...opts,
|
...(opts ?? {}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,26 +6,6 @@
|
|||||||
// App's common configuration for any process (main, renderer, build pipeline, etc.)
|
// App's common configuration for any process (main, renderer, build pipeline, etc.)
|
||||||
import type { ThemeId } from "../renderer/themes/lens-theme";
|
import type { ThemeId } from "../renderer/themes/lens-theme";
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Switch to using isMacInjectable
|
|
||||||
*/
|
|
||||||
export const isMac = process.platform === "darwin";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Switch to using isWindowsInjectable
|
|
||||||
*/
|
|
||||||
export const isWindows = process.platform === "win32";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Switch to using isLinuxInjectable
|
|
||||||
*/
|
|
||||||
export const isLinux = process.platform === "linux";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated switch to using `isDebuggingInjectable`
|
|
||||||
*/
|
|
||||||
export const isDebugging = ["true", "1", "yes", "y", "on"].includes((process.env.DEBUG ?? "").toLowerCase());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Switch to using isTestEnvInjectable
|
* @deprecated Switch to using isTestEnvInjectable
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -15,6 +15,12 @@ import type { ResourceApplyingStack } from "../../common/k8s/resource-stack";
|
|||||||
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||||
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
import type { KubernetesCluster } from "./catalog";
|
import type { KubernetesCluster } from "./catalog";
|
||||||
|
import type { KubeApiDataFrom, KubeObjectStoreOptions } from "../../common/k8s-api/kube-object.store";
|
||||||
|
import { KubeObjectStore as InternalKubeObjectStore } from "../../common/k8s-api/kube-object.store";
|
||||||
|
import type { KubeJsonApiDataFor, KubeObject } from "../../common/k8s-api/kube-object";
|
||||||
|
import type { KubeApi } from "../../common/k8s-api/kube-api";
|
||||||
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context";
|
||||||
|
|
||||||
export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable);
|
export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable);
|
||||||
export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable);
|
export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable);
|
||||||
@ -72,8 +78,44 @@ export {
|
|||||||
type KubeJsonApiData,
|
type KubeJsonApiData,
|
||||||
} from "../../common/k8s-api/kube-json-api";
|
} from "../../common/k8s-api/kube-json-api";
|
||||||
|
|
||||||
|
export abstract class KubeObjectStore<
|
||||||
|
K extends KubeObject = KubeObject,
|
||||||
|
A extends KubeApi<K, D> = KubeApi<K, KubeJsonApiDataFor<K>>,
|
||||||
|
D extends KubeJsonApiDataFor<K> = KubeApiDataFrom<K, A>,
|
||||||
|
> extends InternalKubeObjectStore<K, A, D> {
|
||||||
|
/**
|
||||||
|
* @deprecated This is no longer used and shouldn't have been every really used
|
||||||
|
*/
|
||||||
|
static readonly context = {
|
||||||
|
set: (ctx: ClusterContext) => {
|
||||||
|
console.warn("Setting KubeObjectStore.context is no longer supported");
|
||||||
|
void ctx;
|
||||||
|
},
|
||||||
|
get: () => asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
|
};
|
||||||
|
|
||||||
|
get context() {
|
||||||
|
return this.dependencies.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(api: A, opts?: KubeObjectStoreOptions);
|
||||||
|
/**
|
||||||
|
* @deprecated Supply API instance through constructor
|
||||||
|
*/
|
||||||
|
constructor();
|
||||||
|
constructor(api?: A, opts?: KubeObjectStoreOptions) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
context: asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
api!,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
KubeObjectStore,
|
|
||||||
type JsonPatch,
|
type JsonPatch,
|
||||||
type KubeObjectStoreLoadAllParams,
|
type KubeObjectStoreLoadAllParams,
|
||||||
type KubeObjectStoreLoadingParams,
|
type KubeObjectStoreLoadingParams,
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import type { KubeResource } from "../../common/rbac";
|
import type { KubeResource } from "../../common/rbac";
|
||||||
import isAllowedResourceInjectable from "../../common/utils/is-allowed-resource.injectable";
|
import { apiResourceRecord } from "../../common/rbac";
|
||||||
import { castArray } from "lodash/fp";
|
|
||||||
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
import clusterRoleBindingApiInjectable from "../../common/k8s-api/endpoints/cluster-role-binding.api.injectable";
|
import clusterRoleBindingApiInjectable from "../../common/k8s-api/endpoints/cluster-role-binding.api.injectable";
|
||||||
import clusterRoleApiInjectable from "../../common/k8s-api/endpoints/cluster-role.api.injectable";
|
import clusterRoleApiInjectable from "../../common/k8s-api/endpoints/cluster-role.api.injectable";
|
||||||
@ -37,13 +36,22 @@ import namespaceApiInjectable from "../../common/k8s-api/endpoints/namespace.api
|
|||||||
import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable";
|
import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable";
|
||||||
import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable";
|
import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable";
|
||||||
import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
|
import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
|
||||||
|
import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
||||||
|
|
||||||
export function isAllowedResource(resource: KubeResource | KubeResource[]) {
|
export function isAllowedResource(resources: KubeResource | KubeResource[]) {
|
||||||
const resources = castArray(resource);
|
|
||||||
const di = getLegacyGlobalDiForExtensionApi();
|
const di = getLegacyGlobalDiForExtensionApi();
|
||||||
|
|
||||||
return resources.every((resourceName: any) => {
|
return [resources].flat().every((resourceName) => {
|
||||||
const _isAllowedResource = di.inject(isAllowedResourceInjectable, resourceName);
|
const resource = apiResourceRecord[resourceName];
|
||||||
|
|
||||||
|
if (!resource) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _isAllowedResource = di.inject(shouldShowResourceInjectionToken, {
|
||||||
|
apiName: resourceName,
|
||||||
|
group: resource.group,
|
||||||
|
});
|
||||||
|
|
||||||
// Note: Legacy isAllowedResource does not advertise reactivity
|
// Note: Legacy isAllowedResource does not advertise reactivity
|
||||||
return _isAllowedResource.get();
|
return _isAllowedResource.get();
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injec
|
|||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||||
import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable";
|
import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable";
|
||||||
|
import createClusterInjectable from "../../renderer/cluster/create-cluster.injectable";
|
||||||
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable";
|
|
||||||
|
|
||||||
describe("opening catalog entity details panel", () => {
|
describe("opening catalog entity details panel", () => {
|
||||||
let builder: ApplicationBuilder;
|
let builder: ApplicationBuilder;
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import kubectlBinaryNameInjectable from "../../../main/kubectl/binary-name.injec
|
|||||||
import kubectlDownloadingNormalizedArchInjectable from "../../../main/kubectl/normalized-arch.injectable";
|
import kubectlDownloadingNormalizedArchInjectable from "../../../main/kubectl/normalized-arch.injectable";
|
||||||
import openDeleteClusterDialogInjectable, { type OpenDeleteClusterDialog } from "../../../renderer/components/delete-cluster-dialog/open.injectable";
|
import openDeleteClusterDialogInjectable, { type OpenDeleteClusterDialog } from "../../../renderer/components/delete-cluster-dialog/open.injectable";
|
||||||
import { type ApplicationBuilder, getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
|
import { type ApplicationBuilder, getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
|
||||||
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
|
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||||
@ -90,7 +89,6 @@ describe("Deleting a cluster", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.beforeWindowStart((windowDi) => {
|
builder.beforeWindowStart((windowDi) => {
|
||||||
windowDi.override(storesAndApisCanBeCreatedInjectable, () => true);
|
|
||||||
openDeleteClusterDialog = windowDi.inject(openDeleteClusterDialogInjectable);
|
openDeleteClusterDialog = windowDi.inject(openDeleteClusterDialogInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -98,7 +98,10 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.allowKubeResource("namespaces");
|
builder.allowKubeResource({
|
||||||
|
apiName: "namespaces",
|
||||||
|
group: "v1",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when navigating to namespaces", () => {
|
describe("when navigating to namespaces", () => {
|
||||||
|
|||||||
@ -42,7 +42,10 @@ describe("cluster/namespaces - edit namespaces from previously opened tab", () =
|
|||||||
windowDi.override(callForResourceInjectable, () => callForNamespaceMock);
|
windowDi.override(callForResourceInjectable, () => callForNamespaceMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.allowKubeResource("namespaces");
|
builder.allowKubeResource({
|
||||||
|
apiName: "namespaces",
|
||||||
|
group: "v1",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("given tab was previously opened, when application is started", () => {
|
describe("given tab was previously opened, when application is started", () => {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
|
import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
||||||
import initClusterStoreInjectable from "../../store/renderer/init.injectable";
|
import initClusterStoreInjectable from "../../store/renderer/init.injectable";
|
||||||
import requestInitialClusterStatesInjectable from "./request-initial.injectable";
|
import requestInitialClusterStatesInjectable from "./request-initial.injectable";
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ const setupClusterStateSyncInjectable = getInjectable({
|
|||||||
},
|
},
|
||||||
runAfter: di.inject(initClusterStoreInjectable),
|
runAfter: di.inject(initClusterStoreInjectable),
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupClusterStateSyncInjectable;
|
export default setupClusterStateSyncInjectable;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
||||||
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
|
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
|
||||||
|
|
||||||
const initClusterStoreInjectable = getInjectable({
|
const initClusterStoreInjectable = getInjectable({
|
||||||
@ -18,7 +18,7 @@ const initClusterStoreInjectable = getInjectable({
|
|||||||
},
|
},
|
||||||
runAfter: di.inject(initUserStoreInjectable),
|
runAfter: di.inject(initUserStoreInjectable),
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default initClusterStoreInjectable;
|
export default initClusterStoreInjectable;
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import { sidebarItemsInjectionToken } from "../../renderer/components/layout/sid
|
|||||||
import { computed, runInAction } from "mobx";
|
import { computed, runInAction } from "mobx";
|
||||||
import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token";
|
import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import isAllowedResourceInjectable from "../../common/utils/is-allowed-resource.injectable";
|
|
||||||
import { frontEndRouteInjectionToken } from "../../common/front-end-routing/front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../common/front-end-routing/front-end-route-injection-token";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
||||||
|
import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
||||||
|
|
||||||
describe("cluster - visibility of sidebar items", () => {
|
describe("cluster - visibility of sidebar items", () => {
|
||||||
let builder: ApplicationBuilder;
|
let builder: ApplicationBuilder;
|
||||||
@ -50,7 +50,10 @@ describe("cluster - visibility of sidebar items", () => {
|
|||||||
|
|
||||||
describe("when kube resource becomes allowed", () => {
|
describe("when kube resource becomes allowed", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
builder.allowKubeResource("namespaces");
|
builder.allowKubeResource({
|
||||||
|
apiName: "namespaces",
|
||||||
|
group: "v1",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
@ -69,20 +72,14 @@ describe("cluster - visibility of sidebar items", () => {
|
|||||||
const testRouteInjectable = getInjectable({
|
const testRouteInjectable = getInjectable({
|
||||||
id: "some-route-injectable-id",
|
id: "some-route-injectable-id",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => ({
|
||||||
const someKubeResourceName = "namespaces";
|
|
||||||
|
|
||||||
const kubeResourceIsAllowed = di.inject(
|
|
||||||
isAllowedResourceInjectable,
|
|
||||||
someKubeResourceName,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
path: "/some-child-page",
|
path: "/some-child-page",
|
||||||
isEnabled: kubeResourceIsAllowed,
|
|
||||||
clusterFrame: true,
|
clusterFrame: true,
|
||||||
};
|
isEnabled: di.inject(shouldShowResourceInjectionToken, {
|
||||||
},
|
apiName: "namespaces",
|
||||||
|
group: "v1",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
injectionToken: frontEndRouteInjectionToken,
|
injectionToken: frontEndRouteInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,7 +13,10 @@ describe("workload overview", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
|
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
|
||||||
applicationBuilder.allowKubeResource("pods");
|
applicationBuilder.allowKubeResource({
|
||||||
|
apiName: "pods",
|
||||||
|
group: "v1",
|
||||||
|
});
|
||||||
rendered = await applicationBuilder.render();
|
rendered = await applicationBuilder.render();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import type { Cluster } from "../../common/cluster/cluster";
|
|||||||
import navigateToEntitySettingsInjectable from "../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable";
|
import navigateToEntitySettingsInjectable from "../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable";
|
||||||
import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable";
|
import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable";
|
||||||
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable";
|
import createClusterInjectable from "../../renderer/cluster/create-cluster.injectable";
|
||||||
|
|
||||||
describe("Showing correct entity settings", () => {
|
describe("Showing correct entity settings", () => {
|
||||||
let builder: ApplicationBuilder;
|
let builder: ApplicationBuilder;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import fileSystemProvisionerStoreInjectable from "../../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable";
|
import fileSystemProvisionerStoreInjectable from "../../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../../../renderer/before-frame-starts/tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../../../renderer/before-frame-starts/tokens";
|
||||||
|
|
||||||
const initFileSystemProvisionerStoreInjectable = getInjectable({
|
const initFileSystemProvisionerStoreInjectable = getInjectable({
|
||||||
id: "init-file-system-provisioner-store",
|
id: "init-file-system-provisioner-store",
|
||||||
@ -16,7 +16,7 @@ const initFileSystemProvisionerStoreInjectable = getInjectable({
|
|||||||
store.load();
|
store.load();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default initFileSystemProvisionerStoreInjectable;
|
export default initFileSystemProvisionerStoreInjectable;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
|
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
||||||
import initClusterStoreInjectable from "../../../cluster/store/renderer/init.injectable";
|
import initClusterStoreInjectable from "../../../cluster/store/renderer/init.injectable";
|
||||||
|
|
||||||
const initHotbarStoreInjectable = getInjectable({
|
const initHotbarStoreInjectable = getInjectable({
|
||||||
@ -18,7 +18,7 @@ const initHotbarStoreInjectable = getInjectable({
|
|||||||
},
|
},
|
||||||
runAfter: di.inject(initClusterStoreInjectable),
|
runAfter: di.inject(initClusterStoreInjectable),
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default initHotbarStoreInjectable;
|
export default initHotbarStoreInjectable;
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
|
||||||
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
|
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
|
||||||
import systemThemeConfigurationInjectable from "../../../../renderer/themes/system-theme.injectable";
|
import systemThemeConfigurationInjectable from "../../../../renderer/themes/system-theme.injectable";
|
||||||
import requestInitialSystemThemeTypeInjectable from "./request-initial.injectable";
|
import requestInitialSystemThemeTypeInjectable from "./request-initial.injectable";
|
||||||
@ -20,7 +20,7 @@ const initializeSystemThemeTypeInjectable = getInjectable({
|
|||||||
},
|
},
|
||||||
runAfter: di.inject(initUserStoreInjectable),
|
runAfter: di.inject(initUserStoreInjectable),
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default initializeSystemThemeTypeInjectable;
|
export default initializeSystemThemeTypeInjectable;
|
||||||
|
|||||||
@ -3,22 +3,23 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import configurePackages from "./common/configure-packages";
|
|
||||||
import { configure } from "mobx";
|
import { configure } from "mobx";
|
||||||
import { setImmediate } from "timers";
|
import { setImmediate } from "timers";
|
||||||
import { TextEncoder, TextDecoder as TextDecoderNode } from "util";
|
import { TextEncoder, TextDecoder as TextDecoderNode } from "util";
|
||||||
import glob from "glob";
|
import glob from "glob";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { enableMapSet, setAutoFreeze } from "immer";
|
||||||
// setup default configuration for external npm-packages
|
|
||||||
configurePackages();
|
|
||||||
|
|
||||||
configure({
|
configure({
|
||||||
// Needed because we want to use jest.spyOn()
|
// Needed because we want to use jest.spyOn()
|
||||||
// ref https://github.com/mobxjs/mobx/issues/2784
|
// ref https://github.com/mobxjs/mobx/issues/2784
|
||||||
safeDescriptors: false,
|
safeDescriptors: false,
|
||||||
|
enforceActions: "never",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setAutoFreeze(false); // allow to merge mobx observables
|
||||||
|
enableMapSet(); // allow to merge maps and sets
|
||||||
|
|
||||||
// Mock __non_webpack_require__ for tests
|
// Mock __non_webpack_require__ for tests
|
||||||
globalThis.__non_webpack_require__ = jest.fn();
|
globalThis.__non_webpack_require__ = jest.fn();
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
|||||||
import type { CreateCluster } from "../../common/cluster/create-cluster-injection-token";
|
import type { CreateCluster } from "../../common/cluster/create-cluster-injection-token";
|
||||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||||
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
||||||
import authorizationNamespaceReviewInjectable from "../../common/cluster/authorization-namespace-review.injectable";
|
import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
|
||||||
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
||||||
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
||||||
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||||
@ -20,8 +20,6 @@ import directoryForTempInjectable from "../../common/app-paths/directory-for-tem
|
|||||||
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
|
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
|
||||||
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
|
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
|
||||||
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
|
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
|
||||||
import { apiResourceRecord, apiResources } from "../../common/rbac";
|
|
||||||
import listApiResourcesInjectable from "../../common/cluster/list-api-resources.injectable";
|
|
||||||
import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable";
|
import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable";
|
||||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||||
import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable";
|
import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable";
|
||||||
@ -46,8 +44,7 @@ describe("create clusters", () => {
|
|||||||
di.override(normalizedPlatformInjectable, () => "darwin");
|
di.override(normalizedPlatformInjectable, () => "darwin");
|
||||||
di.override(broadcastMessageInjectable, () => async () => {});
|
di.override(broadcastMessageInjectable, () => async () => {});
|
||||||
di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true));
|
di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true));
|
||||||
di.override(authorizationNamespaceReviewInjectable, () => () => () => Promise.resolve(Object.keys(apiResourceRecord)));
|
di.override(requestNamespaceListPermissionsForInjectable, () => () => async () => () => true);
|
||||||
di.override(listApiResourcesInjectable, () => () => () => Promise.resolve(apiResources));
|
|
||||||
di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
|
di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
|
||||||
di.override(createContextHandlerInjectable, () => (cluster) => ({
|
di.override(createContextHandlerInjectable, () => (cluster) => ({
|
||||||
restartServer: jest.fn(),
|
restartServer: jest.fn(),
|
||||||
|
|||||||
@ -210,7 +210,7 @@ export class ClusterManager {
|
|||||||
cluster.contextName = entity.spec.kubeconfigContext;
|
cluster.contextName = entity.spec.kubeconfigContext;
|
||||||
|
|
||||||
if (entity.spec.accessibleNamespaces) {
|
if (entity.spec.accessibleNamespaces) {
|
||||||
cluster.accessibleNamespaces = entity.spec.accessibleNamespaces;
|
cluster.accessibleNamespaces.replace(entity.spec.accessibleNamespaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.spec.metrics) {
|
if (entity.spec.metrics) {
|
||||||
|
|||||||
@ -2,15 +2,20 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
import { allowedResourcesInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
||||||
|
import type { KubeApiResourceDescriptor } from "../../common/rbac";
|
||||||
|
import { formatKubeApiResource } from "../../common/rbac";
|
||||||
|
|
||||||
// TODO: Figure out implementation for this later.
|
// TODO: Figure out implementation for this later.
|
||||||
const allowedResourcesInjectable = getInjectable({
|
const allowedResourcesInjectable = getInjectable({
|
||||||
id: "allowed-resources",
|
id: "allowed-resources",
|
||||||
instantiate: () => computed(() => new Set<string>()),
|
instantiate: () => computed(() => false),
|
||||||
injectionToken: allowedResourcesInjectionToken,
|
injectionToken: shouldShowResourceInjectionToken,
|
||||||
|
lifecycle: lifecycleEnum.keyedSingleton({
|
||||||
|
getInstanceKey: (di, resource: KubeApiResourceDescriptor) => formatKubeApiResource(resource),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default allowedResourcesInjectable;
|
export default allowedResourcesInjectable;
|
||||||
|
|||||||
@ -11,14 +11,14 @@ import createKubectlInjectable from "../kubectl/create-kubectl.injectable";
|
|||||||
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
||||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||||
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
||||||
import createAuthorizationNamespaceReview from "../../common/cluster/authorization-namespace-review.injectable";
|
|
||||||
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
||||||
import createListApiResourcesInjectable from "../../common/cluster/list-api-resources.injectable";
|
import createListApiResourcesInjectable from "../../common/cluster/request-api-resources.injectable";
|
||||||
import loggerInjectable from "../../common/logger.injectable";
|
import loggerInjectable from "../../common/logger.injectable";
|
||||||
import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable";
|
import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable";
|
||||||
import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable";
|
import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable";
|
||||||
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
|
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
|
||||||
import loadConfigfromFileInjectable from "../../common/kube-helpers/load-config-from-file.injectable";
|
import loadConfigfromFileInjectable from "../../common/kube-helpers/load-config-from-file.injectable";
|
||||||
|
import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
|
||||||
|
|
||||||
const createClusterInjectable = getInjectable({
|
const createClusterInjectable = getInjectable({
|
||||||
id: "create-cluster",
|
id: "create-cluster",
|
||||||
@ -30,8 +30,8 @@ const createClusterInjectable = getInjectable({
|
|||||||
createKubectl: di.inject(createKubectlInjectable),
|
createKubectl: di.inject(createKubectlInjectable),
|
||||||
createContextHandler: di.inject(createContextHandlerInjectable),
|
createContextHandler: di.inject(createContextHandlerInjectable),
|
||||||
createAuthorizationReview: di.inject(authorizationReviewInjectable),
|
createAuthorizationReview: di.inject(authorizationReviewInjectable),
|
||||||
createAuthorizationNamespaceReview: di.inject(createAuthorizationNamespaceReview),
|
requestNamespaceListPermissionsFor: di.inject(requestNamespaceListPermissionsForInjectable),
|
||||||
createListApiResources: di.inject(createListApiResourcesInjectable),
|
requestApiResources: di.inject(createListApiResourcesInjectable),
|
||||||
createListNamespaces: di.inject(listNamespacesInjectable),
|
createListNamespaces: di.inject(listNamespacesInjectable),
|
||||||
logger: di.inject(loggerInjectable),
|
logger: di.inject(loggerInjectable),
|
||||||
detectorRegistry: di.inject(detectorRegistryInjectable),
|
detectorRegistry: di.inject(detectorRegistryInjectable),
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { ContentSource, ElectronWindowTitleBarStyle } from "./create-electron-window.injectable";
|
import type { ContentSource, ElectronWindowTitleBarStyle } from "./create-electron-window.injectable";
|
||||||
import createElectronWindowForInjectable from "./create-electron-window.injectable";
|
import createElectronWindowForInjectable from "./create-electron-window.injectable";
|
||||||
import assert from "assert";
|
|
||||||
import type { ClusterFrameInfo } from "../../../../common/cluster-frames";
|
import type { ClusterFrameInfo } from "../../../../common/cluster-frames";
|
||||||
|
|
||||||
export interface ElectronWindow {
|
export interface ElectronWindow {
|
||||||
@ -69,7 +68,9 @@ const createLensWindowInjectable = getInjectable({
|
|||||||
let windowIsStarting = false;
|
let windowIsStarting = false;
|
||||||
|
|
||||||
const showWindow = () => {
|
const showWindow = () => {
|
||||||
assert(browserWindow);
|
if (!browserWindow) {
|
||||||
|
throw new Error("Cannot show browserWindow, does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
browserWindow.show();
|
browserWindow.show();
|
||||||
windowIsShown = true;
|
windowIsShown = true;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { customMonacoThemeInjectionToken } from "../../components/monaco-editor";
|
import { customMonacoThemeInjectionToken } from "../../components/monaco-editor";
|
||||||
import addNewMonacoThemeInjectable from "../../monaco/add-new-theme.injectable";
|
import addNewMonacoThemeInjectable from "../../monaco/add-new-theme.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../tokens";
|
||||||
|
|
||||||
const loadMonacoThemesInjectable = getInjectable({
|
const loadMonacoThemesInjectable = getInjectable({
|
||||||
id: "load-monaco-themes",
|
id: "load-monaco-themes",
|
||||||
@ -18,7 +18,7 @@ const loadMonacoThemesInjectable = getInjectable({
|
|||||||
customThemes.forEach(addNewMonacoTheme);
|
customThemes.forEach(addNewMonacoTheme);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default loadMonacoThemesInjectable;
|
export default loadMonacoThemesInjectable;
|
||||||
|
|||||||
@ -9,7 +9,9 @@ import { CustomResourceStore } from "../../../common/k8s-api/api-manager/resourc
|
|||||||
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
||||||
import { KubeApi } from "../../../common/k8s-api/kube-api";
|
import { KubeApi } from "../../../common/k8s-api/kube-api";
|
||||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||||
import { beforeClusterFrameStartsInjectionToken } from "../tokens";
|
import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
|
||||||
|
import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store";
|
||||||
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
|
||||||
const setupAutoRegistrationInjectable = getInjectable({
|
const setupAutoRegistrationInjectable = getInjectable({
|
||||||
id: "setup-auto-registration",
|
id: "setup-auto-registration",
|
||||||
@ -19,6 +21,9 @@ const setupAutoRegistrationInjectable = getInjectable({
|
|||||||
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
|
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
|
||||||
const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
|
const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
|
||||||
const beforeApiManagerInitializationApis: KubeApi[] = [];
|
const beforeApiManagerInitializationApis: KubeApi[] = [];
|
||||||
|
const deps: KubeObjectStoreDependencies = {
|
||||||
|
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
|
};
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
|
|
||||||
const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
|
const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
|
||||||
@ -43,7 +48,7 @@ const setupAutoRegistrationInjectable = getInjectable({
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
if (!apiManager.getStore(api)) {
|
if (!apiManager.getStore(api)) {
|
||||||
apiManager.registerStore(new CustomResourceStore(api));
|
apiManager.registerStore(new CustomResourceStore(deps, api));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const autoInitKubeApi = (api: KubeApi) => {
|
const autoInitKubeApi = (api: KubeApi) => {
|
||||||
@ -66,6 +71,7 @@ const setupAutoRegistrationInjectable = getInjectable({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NOTE: this MUST happen after the event emitter listeners are registered
|
||||||
const apiManager = di.inject(apiManagerInjectable);
|
const apiManager = di.inject(apiManagerInjectable);
|
||||||
|
|
||||||
beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
|
beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
|
||||||
@ -73,7 +79,7 @@ const setupAutoRegistrationInjectable = getInjectable({
|
|||||||
initialized = true;
|
initialized = true;
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeClusterFrameStartsInjectionToken,
|
injectionToken: beforeClusterFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupAutoRegistrationInjectable;
|
export default setupAutoRegistrationInjectable;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { reaction } from "mobx";
|
|||||||
import { currentClusterMessageChannel } from "../../../common/cluster/current-cluster-channel";
|
import { currentClusterMessageChannel } from "../../../common/cluster/current-cluster-channel";
|
||||||
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
|
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
|
||||||
import matchedClusterIdInjectable from "../../navigation/matched-cluster-id.injectable";
|
import matchedClusterIdInjectable from "../../navigation/matched-cluster-id.injectable";
|
||||||
import { beforeMainFrameStartsInjectionToken } from "../tokens";
|
import { beforeMainFrameStartsFirstInjectionToken } from "../tokens";
|
||||||
|
|
||||||
const setupCurrentClusterBroadcastInjectable = getInjectable({
|
const setupCurrentClusterBroadcastInjectable = getInjectable({
|
||||||
id: "setup-current-cluster-broadcast",
|
id: "setup-current-cluster-broadcast",
|
||||||
@ -26,7 +26,7 @@ const setupCurrentClusterBroadcastInjectable = getInjectable({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeMainFrameStartsInjectionToken,
|
injectionToken: beforeMainFrameStartsFirstInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupCurrentClusterBroadcastInjectable;
|
export default setupCurrentClusterBroadcastInjectable;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import isLinuxInjectable from "../../../common/vars/is-linux.injectable";
|
|||||||
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||||
import openPathPickingDialogInjectable from "../../../features/path-picking-dialog/renderer/pick-paths.injectable";
|
import openPathPickingDialogInjectable from "../../../features/path-picking-dialog/renderer/pick-paths.injectable";
|
||||||
import addSyncEntriesInjectable from "../../initializers/add-sync-entries.injectable";
|
import addSyncEntriesInjectable from "../../initializers/add-sync-entries.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../tokens";
|
||||||
|
|
||||||
const setupKubernetesClusterCatalogAddMenuListenerInjectable = getInjectable({
|
const setupKubernetesClusterCatalogAddMenuListenerInjectable = getInjectable({
|
||||||
id: "setup-kubernetes-cluster-catalog-add-menu-listener",
|
id: "setup-kubernetes-cluster-catalog-add-menu-listener",
|
||||||
@ -75,7 +75,7 @@ const setupKubernetesClusterCatalogAddMenuListenerInjectable = getInjectable({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupKubernetesClusterCatalogAddMenuListenerInjectable;
|
export default setupKubernetesClusterCatalogAddMenuListenerInjectable;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import readFileInjectable from "../../../common/fs/read-file.injectable";
|
|||||||
import { loadConfigFromString } from "../../../common/kube-helpers";
|
import { loadConfigFromString } from "../../../common/kube-helpers";
|
||||||
import loggerInjectable from "../../../common/logger.injectable";
|
import loggerInjectable from "../../../common/logger.injectable";
|
||||||
import openDeleteClusterDialogInjectable from "../../components/delete-cluster-dialog/open.injectable";
|
import openDeleteClusterDialogInjectable from "../../components/delete-cluster-dialog/open.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../tokens";
|
||||||
|
|
||||||
const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
|
const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
|
||||||
id: "setup-kubernetes-cluster-context-menu-open",
|
id: "setup-kubernetes-cluster-context-menu-open",
|
||||||
@ -50,7 +50,7 @@ const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupKubernetesClusterContextMenuOpenInjectable;
|
export default setupKubernetesClusterContextMenuOpenInjectable;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import isMacInjectable from "../../../common/vars/is-mac.injectable";
|
import isMacInjectable from "../../../common/vars/is-mac.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../tokens";
|
||||||
|
|
||||||
const setupRootMacClassnameInjectable = getInjectable({
|
const setupRootMacClassnameInjectable = getInjectable({
|
||||||
id: "setup-root-mac-classname",
|
id: "setup-root-mac-classname",
|
||||||
@ -17,7 +17,7 @@ const setupRootMacClassnameInjectable = getInjectable({
|
|||||||
rootElem?.classList.toggle("is-mac", isMac);
|
rootElem?.classList.toggle("is-mac", isMac);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupRootMacClassnameInjectable;
|
export default setupRootMacClassnameInjectable;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import initializeSentryReportingWithInjectable from "../../../common/error-reporting/initialize-sentry-reporting.injectable";
|
import initializeSentryReportingWithInjectable from "../../../common/error-reporting/initialize-sentry-reporting.injectable";
|
||||||
import { beforeMainFrameStartsInjectionToken } from "../tokens";
|
import { beforeMainFrameStartsFirstInjectionToken } from "../tokens";
|
||||||
import { init } from "@sentry/electron/renderer";
|
import { init } from "@sentry/electron/renderer";
|
||||||
|
|
||||||
const setupSentryInjectable = getInjectable({
|
const setupSentryInjectable = getInjectable({
|
||||||
@ -17,7 +17,7 @@ const setupSentryInjectable = getInjectable({
|
|||||||
initializeSentryReportingWith(init);
|
initializeSentryReportingWith(init);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeMainFrameStartsInjectionToken,
|
injectionToken: beforeMainFrameStartsFirstInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupSentryInjectable;
|
export default setupSentryInjectable;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import React from "react";
|
|||||||
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
|
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
|
||||||
import { WeblinkAddCommand } from "../../components/catalog-entities/weblink-add-command";
|
import { WeblinkAddCommand } from "../../components/catalog-entities/weblink-add-command";
|
||||||
import commandOverlayInjectable from "../../components/command-palette/command-overlay.injectable";
|
import commandOverlayInjectable from "../../components/command-palette/command-overlay.injectable";
|
||||||
import { beforeFrameStartsInjectionToken } from "../tokens";
|
import { beforeFrameStartsSecondInjectionToken } from "../tokens";
|
||||||
|
|
||||||
const setupWeblickContextMenuOpenInjectable = getInjectable({
|
const setupWeblickContextMenuOpenInjectable = getInjectable({
|
||||||
id: "setup-weblick-context-menu-open",
|
id: "setup-weblick-context-menu-open",
|
||||||
@ -28,7 +28,7 @@ const setupWeblickContextMenuOpenInjectable = getInjectable({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectionToken: beforeFrameStartsInjectionToken,
|
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default setupWeblickContextMenuOpenInjectable;
|
export default setupWeblickContextMenuOpenInjectable;
|
||||||
|
|||||||
@ -7,20 +7,30 @@ import type { Runnable } from "../../common/runnable/run-many-for";
|
|||||||
|
|
||||||
// NOTE: these are run before any other token, mostly to set up things that all other runnables need
|
// NOTE: these are run before any other token, mostly to set up things that all other runnables need
|
||||||
export const beforeFrameStartsFirstInjectionToken = getInjectionToken<Runnable>({
|
export const beforeFrameStartsFirstInjectionToken = getInjectionToken<Runnable>({
|
||||||
id: "even-before-frame-starts",
|
id: "before-frame-starts-first",
|
||||||
});
|
});
|
||||||
|
|
||||||
// NOTE: these are only run when process.isMainFrame === true
|
// NOTE: these are only run when process.isMainFrame === true
|
||||||
export const beforeMainFrameStartsInjectionToken = getInjectionToken<Runnable>({
|
export const beforeMainFrameStartsFirstInjectionToken = getInjectionToken<Runnable>({
|
||||||
id: "even-before-main-frame-starts",
|
id: "before-main-frame-starts-first",
|
||||||
});
|
});
|
||||||
|
|
||||||
// NOTE: these are only run when process.isMainFrame === false
|
// NOTE: these are only run when process.isMainFrame === false
|
||||||
export const beforeClusterFrameStartsInjectionToken = getInjectionToken<Runnable>({
|
export const beforeClusterFrameStartsFirstInjectionToken = getInjectionToken<Runnable>({
|
||||||
id: "even-before-cluster-frame-starts",
|
id: "before-cluster-frame-starts-first",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const beforeFrameStartsInjectionToken = getInjectionToken<Runnable>({
|
export const beforeFrameStartsSecondInjectionToken = getInjectionToken<Runnable>({
|
||||||
id: "before-frame-starts",
|
id: "before-frame-starts-second",
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: these are only run when process.isMainFrame === true
|
||||||
|
export const beforeMainFrameStartsSecondInjectionToken = getInjectionToken<Runnable>({
|
||||||
|
id: "before-main-frame-starts-second",
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: these are only run when process.isMainFrame === false
|
||||||
|
export const beforeClusterFrameStartsSecondInjectionToken = getInjectionToken<Runnable>({
|
||||||
|
id: "before-cluster-frame-starts-second",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +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 { comparer, computed } from "mobx";
|
|
||||||
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
|
||||||
import { allowedResourcesInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
|
||||||
|
|
||||||
const allowedResourcesInjectable = getInjectable({
|
|
||||||
id: "allowed-resources",
|
|
||||||
|
|
||||||
instantiate: (di) => {
|
|
||||||
const cluster = di.inject(hostedClusterInjectable);
|
|
||||||
|
|
||||||
return computed(() => new Set(cluster?.allowedResources), {
|
|
||||||
// This needs to be here so that during refresh changes are only propogated when necessary
|
|
||||||
equals: (cur, prev) => comparer.structural(cur, prev),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
injectionToken: allowedResourcesInjectionToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default allowedResourcesInjectable;
|
|
||||||
@ -1,25 +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 { ClusterFrameContext } from "./cluster-frame-context";
|
|
||||||
import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
|
|
||||||
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
|
||||||
import assert from "assert";
|
|
||||||
|
|
||||||
const clusterFrameContextInjectable = getInjectable({
|
|
||||||
id: "cluster-frame-context",
|
|
||||||
|
|
||||||
instantiate: (di) => {
|
|
||||||
const cluster = di.inject(hostedClusterInjectable);
|
|
||||||
|
|
||||||
assert(cluster, "This can only be injected within a cluster frame");
|
|
||||||
|
|
||||||
return new ClusterFrameContext(cluster, {
|
|
||||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default clusterFrameContextInjectable;
|
|
||||||
@ -3,44 +3,14 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
/**
|
||||||
import type { NamespaceStore } from "../components/+namespaces/store";
|
* This type is used for KubeObjectStores
|
||||||
import type { ClusterContext } from "../../common/k8s-api/cluster-context";
|
*/
|
||||||
import { computed, makeObservable } from "mobx";
|
export interface ClusterContext {
|
||||||
|
readonly allNamespaces: string[]; // available / allowed namespaces from cluster.ts
|
||||||
|
readonly contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
|
||||||
|
readonly hasSelectedAll: boolean;
|
||||||
|
|
||||||
interface Dependencies {
|
isLoadingAll(namespaces: string[]): boolean;
|
||||||
namespaceStore: NamespaceStore;
|
isGlobalWatchEnabled(): boolean;
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterFrameContext implements ClusterContext {
|
|
||||||
constructor(public cluster: Cluster, private dependencies: Dependencies) {
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get allNamespaces(): string[] {
|
|
||||||
// user given list of namespaces
|
|
||||||
if (this.cluster.accessibleNamespaces.length) {
|
|
||||||
return this.cluster.accessibleNamespaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dependencies.namespaceStore.items.length > 0) {
|
|
||||||
// namespaces from kubernetes api
|
|
||||||
return this.dependencies.namespaceStore.items.map((namespace) => namespace.getName());
|
|
||||||
} else {
|
|
||||||
// fallback to cluster resolved namespaces because we could not load list
|
|
||||||
return this.cluster.allowedNamespaces || [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get contextNamespaces(): string[] {
|
|
||||||
return this.dependencies.namespaceStore.contextNamespaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get hasSelectedAll(): boolean {
|
|
||||||
const namespaces = new Set(this.contextNamespaces);
|
|
||||||
|
|
||||||
return this.allNamespaces?.length > 1
|
|
||||||
&& this.cluster.accessibleNamespaces.length === 0
|
|
||||||
&& this.allNamespaces.every(ns => namespaces.has(ns));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { ClusterContext } from "./cluster-frame-context";
|
||||||
|
|
||||||
|
const clusterFrameContextForClusterScopedResourcesInjectable = getInjectable({
|
||||||
|
id: "cluster-frame-context-for-cluster-scoped-resources",
|
||||||
|
instantiate: (): ClusterContext => ({
|
||||||
|
// This doesn't matter as it is an optimization for namespaced resources only
|
||||||
|
isGlobalWatchEnabled: () => true,
|
||||||
|
// This is always the case for cluster scoped resources
|
||||||
|
isLoadingAll: () => true,
|
||||||
|
allNamespaces: [],
|
||||||
|
contextNamespaces: [],
|
||||||
|
hasSelectedAll: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterFrameContextForClusterScopedResourcesInjectable;
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* 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 { ClusterContext } from "./cluster-frame-context";
|
||||||
|
import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
|
||||||
|
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
import { computed } from "mobx";
|
||||||
|
|
||||||
|
const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
|
||||||
|
id: "cluster-frame-context-for-namespaced-resources",
|
||||||
|
|
||||||
|
instantiate: (di): ClusterContext => {
|
||||||
|
const cluster = di.inject(hostedClusterInjectable);
|
||||||
|
const namespaceStore = di.inject(namespaceStoreInjectable);
|
||||||
|
|
||||||
|
assert(cluster, "This can only be injected within a cluster frame");
|
||||||
|
|
||||||
|
const allNamespaces = computed(() => {
|
||||||
|
// user given list of namespaces
|
||||||
|
if (cluster.accessibleNamespaces.length) {
|
||||||
|
return cluster.accessibleNamespaces.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namespaceStore.items.length > 0) {
|
||||||
|
// namespaces from kubernetes api
|
||||||
|
return namespaceStore.items.map((namespace) => namespace.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback to cluster resolved namespaces because we could not load list
|
||||||
|
return cluster.allowedNamespaces.slice();
|
||||||
|
});
|
||||||
|
const contextNamespaces = computed(() => namespaceStore.contextNamespaces);
|
||||||
|
const hasSelectedAll = computed(() => {
|
||||||
|
const namespaces = new Set(contextNamespaces.get());
|
||||||
|
|
||||||
|
return allNamespaces.get().length > 1
|
||||||
|
&& cluster.accessibleNamespaces.length === 0
|
||||||
|
&& allNamespaces.get().every(ns => namespaces.has(ns));
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLoadingAll: (namespaces) => (
|
||||||
|
allNamespaces.get().length > 1
|
||||||
|
&& cluster.accessibleNamespaces.length === 0
|
||||||
|
&& allNamespaces.get().every(ns => namespaces.includes(ns))
|
||||||
|
),
|
||||||
|
isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled,
|
||||||
|
get allNamespaces() {
|
||||||
|
return allNamespaces.get();
|
||||||
|
},
|
||||||
|
get contextNamespaces() {
|
||||||
|
return contextNamespaces.get();
|
||||||
|
},
|
||||||
|
get hasSelectedAll() {
|
||||||
|
return hasSelectedAll.get();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterFrameContextForNamespacedResourcesInjectable;
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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 { computed } from "mobx";
|
||||||
|
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
||||||
|
import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
||||||
|
import type { KubeApiResourceDescriptor } from "../../common/rbac";
|
||||||
|
import { formatKubeApiResource } from "../../common/rbac";
|
||||||
|
|
||||||
|
const shouldShowResourceInjectable = getInjectable({
|
||||||
|
id: "should-show-resource",
|
||||||
|
instantiate: (di, resource) => {
|
||||||
|
const cluster = di.inject(hostedClusterInjectable);
|
||||||
|
|
||||||
|
return cluster
|
||||||
|
? computed(() => cluster.shouldShowResource(resource))
|
||||||
|
: computed(() => false);
|
||||||
|
},
|
||||||
|
injectionToken: shouldShowResourceInjectionToken,
|
||||||
|
lifecycle: lifecycleEnum.keyedSingleton({
|
||||||
|
getInstanceKey: (di, resource: KubeApiResourceDescriptor) => formatKubeApiResource(resource),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default shouldShowResourceInjectable;
|
||||||
18
src/renderer/cluster/accessible-namespaces.injectable.ts
Normal file
18
src/renderer/cluster/accessible-namespaces.injectable.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* 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 hostedClusterInjectable from "../cluster-frame-context/hosted-cluster.injectable";
|
||||||
|
|
||||||
|
const clusterConfiguredAccessibleNamespacesInjectable = getInjectable({
|
||||||
|
id: "cluster-configured-accessible-namespaces",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const hostedCluster = di.inject(hostedClusterInjectable);
|
||||||
|
|
||||||
|
return computed(() => [...hostedCluster?.accessibleNamespaces ?? []]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterConfiguredAccessibleNamespacesInjectable;
|
||||||
@ -27,9 +27,9 @@ const createClusterInjectable = getInjectable({
|
|||||||
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
|
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
|
||||||
createContextHandler: () => undefined as never,
|
createContextHandler: () => undefined as never,
|
||||||
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
createAuthorizationNamespaceReview: () => { 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."); },
|
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
createListApiResources: ()=> { 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."); },
|
||||||
detectorRegistry: undefined as never,
|
detectorRegistry: undefined as never,
|
||||||
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
};
|
};
|
||||||
@ -13,6 +13,7 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre
|
|||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import nodeStoreInjectable from "../../+nodes/store.injectable";
|
import nodeStoreInjectable from "../../+nodes/store.injectable";
|
||||||
import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
|
||||||
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
|
||||||
const clusterOverviewStoreInjectable = getInjectable({
|
const clusterOverviewStoreInjectable = getInjectable({
|
||||||
id: "cluster-overview-store",
|
id: "cluster-overview-store",
|
||||||
@ -32,6 +33,7 @@ const clusterOverviewStoreInjectable = getInjectable({
|
|||||||
),
|
),
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
nodeStore: di.inject(nodeStoreInjectable),
|
||||||
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
|
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
|
||||||
|
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
}, clusterApi);
|
}, clusterApi);
|
||||||
},
|
},
|
||||||
injectionToken: kubeObjectStoreInjectionToken,
|
injectionToken: kubeObjectStoreInjectionToken,
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { action, observable, reaction, when, makeObservable } from "mobx";
|
import { action, observable, reaction, when, makeObservable } from "mobx";
|
||||||
|
import type { KubeObjectStoreDependencies } from "../../../../common/k8s-api/kube-object.store";
|
||||||
import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
|
import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
|
||||||
import type { Cluster, ClusterApi } from "../../../../common/k8s-api/endpoints";
|
import type { Cluster, ClusterApi } from "../../../../common/k8s-api/endpoints";
|
||||||
import type { StorageLayer } from "../../../utils";
|
import type { StorageLayer } from "../../../utils";
|
||||||
@ -28,7 +29,7 @@ export interface ClusterOverviewStorageState {
|
|||||||
metricNodeRole: MetricNodeRole;
|
metricNodeRole: MetricNodeRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClusterOverviewStoreDependencies {
|
interface ClusterOverviewStoreDependencies extends KubeObjectStoreDependencies {
|
||||||
readonly storage: StorageLayer<ClusterOverviewStorageState>;
|
readonly storage: StorageLayer<ClusterOverviewStorageState>;
|
||||||
readonly nodeStore: NodeStore;
|
readonly nodeStore: NodeStore;
|
||||||
requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames;
|
requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames;
|
||||||
@ -58,7 +59,7 @@ export class ClusterOverviewStore extends KubeObjectStore<Cluster, ClusterApi> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(protected readonly dependencies: ClusterOverviewStoreDependencies, api: ClusterApi) {
|
constructor(protected readonly dependencies: ClusterOverviewStoreDependencies, api: ClusterApi) {
|
||||||
super(api);
|
super(dependencies, api);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user