mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix rerendering of KubeObject views every 30s (#4801)
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
105 files changed
+1442
-709
No files matched your search
@@ -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, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { comparer, computed } from "mobx";
|
||||
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
||||
|
||||
const allowedResourcesInjectable = getInjectable({
|
||||
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),
|
||||
});
|
||||
},
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default allowedResourcesInjectable;
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { getHostedClusterId } from "../../utils";
|
||||
import clusterStoreInjectable from "../cluster-store.injectable";
|
||||
import { getHostedClusterId } from "../utils";
|
||||
import clusterStoreInjectable from "./cluster-store.injectable";
|
||||
|
||||
const hostedClusterInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
@@ -7,7 +7,7 @@ import yaml from "js-yaml";
|
||||
import { formatDuration } from "../../utils";
|
||||
import capitalize from "lodash/capitalize";
|
||||
import { apiBase } from "../index";
|
||||
import { helmChartStore } from "../../../renderer/components/+apps-helm-charts/helm-chart.store";
|
||||
import { helmChartStore } from "../../../renderer/components/+helm-charts/helm-chart.store";
|
||||
import type { ItemObject } from "../../item.store";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { JsonApiData } from "../json-api";
|
||||
|
||||
@@ -16,12 +16,9 @@ import { KubeObjectConstructor, KubeObject, KubeStatus } from "./kube-object";
|
||||
import byline from "byline";
|
||||
import type { IKubeWatchEvent } from "./kube-watch-event";
|
||||
import { KubeJsonApi, KubeJsonApiData } from "./kube-json-api";
|
||||
import { noop } from "../utils";
|
||||
import { noop, WrappedAbortController } from "../utils";
|
||||
import type { RequestInit } from "node-fetch";
|
||||
|
||||
// BUG: https://github.com/mysticatea/abort-controller/pull/22
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import AbortController from "abort-controller";
|
||||
import type AbortController from "abort-controller";
|
||||
import { Agent, AgentOptions } from "https";
|
||||
import type { Patch } from "rfc6902";
|
||||
|
||||
@@ -582,14 +579,7 @@ export class KubeApi<T extends KubeObject> {
|
||||
const { watchId = `${this.kind.toLowerCase()}-${this.watchId++}` } = opts;
|
||||
|
||||
// Create AbortController for this request
|
||||
const abortController = new AbortController();
|
||||
|
||||
// If caller aborts, abort using request's abortController
|
||||
if (opts.abortController) {
|
||||
opts.abortController.signal.addEventListener("abort", () => {
|
||||
abortController.abort();
|
||||
});
|
||||
}
|
||||
const abortController = new WrappedAbortController(opts.abortController);
|
||||
|
||||
abortController.signal.addEventListener("abort", () => {
|
||||
logger.info(`[KUBE-API] watch (${watchId}) aborted ${watchUrl}`);
|
||||
@@ -687,7 +677,6 @@ export class KubeApi<T extends KubeObject> {
|
||||
protected modifyWatchEvent(event: IKubeWatchEvent<KubeJsonApiData>) {
|
||||
if (event.type === "ERROR") {
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ensureObjectSelfLink(this, event.object);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import type { ClusterContext } from "./cluster-context";
|
||||
|
||||
import { action, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||
import { autoBind, noop, rejectPromiseBy } from "../utils";
|
||||
import { autoBind, Disposer, noop, rejectPromiseBy } from "../utils";
|
||||
import { KubeObject, KubeStatus } from "./kube-object";
|
||||
import type { IKubeWatchEvent } from "./kube-watch-event";
|
||||
import { ItemStore } from "../item.store";
|
||||
@@ -378,7 +378,7 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
|
||||
});
|
||||
}
|
||||
|
||||
subscribe({ onLoadFailure, abortController = new AbortController() }: KubeObjectStoreSubscribeParams = {}) {
|
||||
subscribe({ onLoadFailure, abortController = new AbortController() }: KubeObjectStoreSubscribeParams = {}): Disposer {
|
||||
if (this.api.isNamespaced) {
|
||||
Promise.race([rejectPromiseBy(abortController.signal), Promise.all([this.contextReady, this.namespacesReady])])
|
||||
.then(() => {
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import type { RouteProps } from "react-router";
|
||||
import { buildURL } from "../utils/buildUrl";
|
||||
import { appsRoute } from "./apps";
|
||||
import { helmRoute } from "./helm";
|
||||
|
||||
export const helmChartsRoute: RouteProps = {
|
||||
path: `${appsRoute.path}/charts/:repo?/:chartName?`,
|
||||
path: `${helmRoute.path}/charts/:repo?/:chartName?`,
|
||||
};
|
||||
|
||||
export interface HelmChartsRouteParams {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import type { RouteProps } from "react-router";
|
||||
import { buildURL } from "../utils/buildUrl";
|
||||
|
||||
export const appsRoute: RouteProps = {
|
||||
path: "/apps",
|
||||
export const helmRoute: RouteProps = {
|
||||
path: "/helm",
|
||||
};
|
||||
|
||||
export const appsURL = buildURL(appsRoute.path);
|
||||
export const helmURL = buildURL(helmRoute.path);
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
export * from "./add-cluster";
|
||||
export * from "./apps";
|
||||
export * from "./catalog";
|
||||
export * from "./cluster-view";
|
||||
export * from "./cluster";
|
||||
@@ -16,6 +15,7 @@ export * from "./entity-settings";
|
||||
export * from "./events";
|
||||
export * from "./extensions";
|
||||
export * from "./helm-charts";
|
||||
export * from "./helm";
|
||||
export * from "./hpa";
|
||||
export * from "./ingresses";
|
||||
export * from "./limit-ranges";
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import type { RouteProps } from "react-router";
|
||||
import { buildURL } from "../utils/buildUrl";
|
||||
import { appsRoute } from "./apps";
|
||||
import { helmRoute } from "./helm";
|
||||
|
||||
export const releaseRoute: RouteProps = {
|
||||
path: `${appsRoute.path}/releases/:namespace?/:name?`,
|
||||
path: `${helmRoute.path}/releases/:namespace?/:name?`,
|
||||
};
|
||||
|
||||
export interface ReleaseRouteParams {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import AbortController from "abort-controller";
|
||||
|
||||
export class WrappedAbortController extends AbortController {
|
||||
constructor(parent?: AbortController) {
|
||||
super();
|
||||
|
||||
parent?.signal.addEventListener("abort", () => {
|
||||
this.abort();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { ClusterStore } from "../cluster-store/cluster-store";
|
||||
import type { KubeResource } from "../rbac";
|
||||
import { getHostedClusterId } from "./cluster-id-url-parsing";
|
||||
|
||||
export function isAllowedResource(resource: KubeResource | KubeResource[]) {
|
||||
const resources = [resource].flat();
|
||||
const cluster = ClusterStore.getInstance().getById(getHostedClusterId());
|
||||
|
||||
if (!cluster?.allowedResources) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resources.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const allowedResources = new Set(cluster.allowedResources);
|
||||
|
||||
return resources.every(resource => allowedResources.has(resource));
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export function noop<T extends any[]>(...args: T): void {
|
||||
return void args;
|
||||
}
|
||||
|
||||
export * from "./abort-controller";
|
||||
export * from "./app-version";
|
||||
export * from "./autobind";
|
||||
export * from "./camelCase";
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 allowedResourcesInjectable from "../cluster-store/allowed-resources.injectable";
|
||||
import type { KubeResource } from "../rbac";
|
||||
|
||||
export type IsAllowedResource = (resource: KubeResource) => boolean;
|
||||
|
||||
// TODO: This injectable obscures MobX de-referencing. Make it more apparent in usage.
|
||||
const isAllowedResourceInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
const allowedResources = di.inject(allowedResourcesInjectable);
|
||||
|
||||
return (resource: KubeResource) => allowedResources.get().has(resource);
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default isAllowedResourceInjectable;
|
||||
@@ -3,7 +3,14 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export { isAllowedResource } from "../../common/utils/allowed-resource";
|
||||
/**
|
||||
* @deprecated This function never works
|
||||
* @returns false
|
||||
*/
|
||||
export function isAllowedResource(...args: any[]) {
|
||||
return Boolean(void args);
|
||||
}
|
||||
|
||||
export { ResourceStack } from "../../common/k8s/resource-stack";
|
||||
export { apiManager } from "../../common/k8s-api/api-manager";
|
||||
export { KubeApi, forCluster, forRemoteCluster } from "../../common/k8s-api/kube-api";
|
||||
|
||||
@@ -2,8 +2,19 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { KubeResource } from "../../common/rbac";
|
||||
import isAllowedResourceInjectable from "../../common/utils/is-allowed-resource.injectable";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import { castArray } from "lodash/fp";
|
||||
|
||||
export function isAllowedResource(resource: KubeResource | KubeResource[]) {
|
||||
const _isAllowedResource = asLegacyGlobalFunctionForExtensionApi(isAllowedResourceInjectable);
|
||||
|
||||
const resources = castArray(resource);
|
||||
|
||||
return resources.every(x => _isAllowedResource(x));
|
||||
}
|
||||
|
||||
export { isAllowedResource } from "../../common/utils/allowed-resource";
|
||||
export { ResourceStack } from "../../common/k8s/resource-stack";
|
||||
export { apiManager } from "../../common/k8s-api/api-manager";
|
||||
export { KubeObjectStore } from "../../common/k8s-api/kube-object.store";
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { ClusterFrameContext } from "./cluster-frame-context";
|
||||
import namespaceStoreInjectable from "../components/+namespaces/namespace-store/namespace-store.injectable";
|
||||
import hostedClusterInjectable from "../../common/cluster-store/hosted-cluster/hosted-cluster.injectable";
|
||||
import hostedClusterInjectable from "../../common/cluster-store/hosted-cluster.injectable";
|
||||
|
||||
const clusterFrameContextInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { HelmCharts } from "../+apps-helm-charts";
|
||||
import { HelmReleases } from "../+apps-releases";
|
||||
import { helmChartsURL, helmChartsRoute, releaseURL, releaseRoute } from "../../../common/routes";
|
||||
|
||||
@observer
|
||||
export class Apps extends React.Component {
|
||||
static get tabRoutes(): TabLayoutRoute[] {
|
||||
|
||||
return [
|
||||
{
|
||||
title: "Charts",
|
||||
component: HelmCharts,
|
||||
url: helmChartsURL(),
|
||||
routePath: helmChartsRoute.path.toString(),
|
||||
},
|
||||
{
|
||||
title: "Releases",
|
||||
component: HelmReleases,
|
||||
url: releaseURL(),
|
||||
routePath: releaseRoute.path.toString(),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout className="Apps" tabs={Apps.tabRoutes}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./apps";
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { clusterRoute, clusterURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface ClusterSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedClusterSidebarItem = observer(({ isAllowedResource }: Dependencies & ClusterSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="cluster"
|
||||
text="Cluster"
|
||||
isActive={isActiveRoute(clusterRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={clusterURL()}
|
||||
icon={<Icon svg="kube"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const ClusterSidebarItem = withInjectables<Dependencies, ClusterSidebarItemProps>(NonInjectedClusterSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./config";
|
||||
+24
-19
@@ -2,22 +2,24 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { ConfigMaps } from "../+config-maps";
|
||||
import { Secrets } from "../+config-secrets";
|
||||
import { ResourceQuotas } from "../+config-resource-quotas";
|
||||
import { PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { HorizontalPodAutoscalers } from "../+config-autoscalers";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { LimitRanges } from "../+config-limit-ranges";
|
||||
import { ConfigMaps } from "../+config-maps";
|
||||
import { PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
|
||||
import { ResourceQuotas } from "../+config-resource-quotas";
|
||||
import { Secrets } from "../+config-secrets";
|
||||
import isAllowedResourceInjectable, { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import * as routes from "../../../common/routes";
|
||||
|
||||
@observer
|
||||
export class Config extends React.Component {
|
||||
static get tabRoutes(): TabLayoutRoute[] {
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
function getRouteTabs({ isAllowedResource }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: TabLayoutRoute[] = [];
|
||||
|
||||
if (isAllowedResource("configmaps")) {
|
||||
@@ -75,11 +77,14 @@ export class Config extends React.Component {
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout className="Config" tabs={Config.tabRoutes}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const configRoutesInjectable = getInjectable({
|
||||
instantiate: (di) => getRouteTabs({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default configRoutesInjectable;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import configRoutesInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface ConfigRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedConfigRoute = observer(({ routes }: Dependencies & ConfigRouteProps) => (
|
||||
<TabLayout
|
||||
className="Config"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const ConfigRoute = withInjectables<Dependencies, ConfigRouteProps>(NonInjectedConfigRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(configRoutesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { configRoute, configURL } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { TabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import configRoutesInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface ConfigSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedConfigSidebarItem = observer(({ routes }: Dependencies & ConfigSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="config"
|
||||
text="Configuration"
|
||||
isActive={isActiveRoute(configRoute)}
|
||||
isHidden={tabRoutes.length == 0}
|
||||
url={configURL()}
|
||||
icon={<Icon material="list"/>}
|
||||
>
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const ConfigSidebarItem = withInjectables<Dependencies, ConfigSidebarItemProps>(NonInjectedConfigSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(configRoutesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Redirect, Route, Switch } from "react-router";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { CrdList } from "./crd-list";
|
||||
import { CrdResources } from "./crd-resources";
|
||||
import { crdURL, crdDefinitionsRoute, crdResourcesRoute } from "../../../common/routes";
|
||||
|
||||
@observer
|
||||
export class CustomResources extends React.Component {
|
||||
static get tabRoutes(): TabLayoutRoute[] {
|
||||
return [
|
||||
{
|
||||
title: "Definitions",
|
||||
component: CustomResources,
|
||||
url: crdURL(),
|
||||
routePath: String(crdDefinitionsRoute.path),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout>
|
||||
<Switch>
|
||||
<Route component={CrdList} {...crdDefinitionsRoute} exact/>
|
||||
<Route component={CrdResources} {...crdResourcesRoute}/>
|
||||
<Redirect to={crdURL()}/>
|
||||
</Switch>
|
||||
</TabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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, IComputedValue } from "mobx";
|
||||
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
||||
import { getOrInsert } from "../../utils";
|
||||
import customResourceDefinitionsInjectable from "./custom-resources.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
definitions: IComputedValue<CustomResourceDefinition[]>;
|
||||
}
|
||||
|
||||
function getGroupedCustomResourceDefinitions({ definitions }: Dependencies) {
|
||||
return computed(() => {
|
||||
const groups = new Map<string, CustomResourceDefinition[]>();
|
||||
|
||||
for (const crd of definitions.get()) {
|
||||
getOrInsert(groups, crd.getGroup(), []).push(crd);
|
||||
}
|
||||
|
||||
return groups;
|
||||
});
|
||||
}
|
||||
|
||||
const groupedCustomResourceDefinitionsInjectable = getInjectable({
|
||||
instantiate: (di) => getGroupedCustomResourceDefinitions({
|
||||
definitions: di.inject(customResourceDefinitionsInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default groupedCustomResourceDefinitionsInjectable;
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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, IComputedValue } from "mobx";
|
||||
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
||||
import { crdURL, crdDefinitionsRoute } from "../../../common/routes";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { CrdList } from "./crd-list";
|
||||
import { CrdResources } from "./crd-resources";
|
||||
import groupedCustomResourceDefinitionsInjectable from "./grouped-custom-resources.injectable";
|
||||
|
||||
export interface CustomResourceTabLayoutRoute extends TabLayoutRoute {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface CustomResourceGroupTabLayoutRoute extends CustomResourceTabLayoutRoute {
|
||||
subRoutes?: CustomResourceTabLayoutRoute[];
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
customResourcesDefinitions: IComputedValue<Map<string, CustomResourceDefinition[]>>;
|
||||
}
|
||||
|
||||
function getRouteTabs({ customResourcesDefinitions }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: CustomResourceGroupTabLayoutRoute[] = [
|
||||
{
|
||||
id: "definitions",
|
||||
title: "Definitions",
|
||||
component: CrdList,
|
||||
url: crdURL(),
|
||||
routePath: String(crdDefinitionsRoute.path),
|
||||
exact: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (const [group, definitions] of customResourcesDefinitions.get()) {
|
||||
tabs.push({
|
||||
id: `crd-group:${group}`,
|
||||
title: group,
|
||||
routePath: crdURL({ query: { groups: group }}),
|
||||
component: CrdResources,
|
||||
subRoutes: definitions.map(crd => ({
|
||||
id: `crd-resource:${crd.getResourceApiBase()}`,
|
||||
title: crd.getResourceKind(),
|
||||
routePath: crd.getResourceUrl(),
|
||||
component: CrdResources,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
});
|
||||
}
|
||||
|
||||
const customResourcesRouteTabsInjectable = getInjectable({
|
||||
instantiate: (di) => getRouteTabs({
|
||||
customResourcesDefinitions: di.inject(groupedCustomResourceDefinitionsInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default customResourcesRouteTabsInjectable;
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Redirect, Route, Switch } from "react-router";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { crdURL } from "../../../common/routes";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type { CustomResourceGroupTabLayoutRoute } from "./route-tabs.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import customResourcesRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<CustomResourceGroupTabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedCustomResourcesRoute = observer(({ routes }: Dependencies) => (
|
||||
<TabLayout>
|
||||
<Switch>
|
||||
{
|
||||
routes.get().map(({ id, component, routePath, exact }) => (
|
||||
<Route
|
||||
key={id}
|
||||
component={component}
|
||||
path={routePath}
|
||||
exact={exact}
|
||||
/>
|
||||
))
|
||||
}
|
||||
<Redirect to={crdURL()}/>
|
||||
</Switch>
|
||||
</TabLayout>
|
||||
));
|
||||
|
||||
export const CustomResourcesRoute = withInjectables<Dependencies>(NonInjectedCustomResourcesRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(customResourcesRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React, { useEffect } from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import customResourcesRouteTabsInjectable, { type CustomResourceGroupTabLayoutRoute } from "./route-tabs.injectable";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { crdURL, crdRoute } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
||||
import { crdStore } from "./crd.store";
|
||||
import { Spinner } from "../spinner";
|
||||
|
||||
export interface CustomResourcesSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<CustomResourceGroupTabLayoutRoute[]>;
|
||||
isAllowedResource: IsAllowedResource;
|
||||
subscribeStores: SubscribeStores;
|
||||
}
|
||||
|
||||
const NonInjectedCustomResourcesSidebarItem = observer(({ routes, isAllowedResource, subscribeStores }: Dependencies & CustomResourcesSidebarItemProps) => {
|
||||
useEffect(() => subscribeStores([
|
||||
crdStore,
|
||||
]), []);
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="custom-resources"
|
||||
text="Custom Resources"
|
||||
url={crdURL()}
|
||||
isActive={isActiveRoute(crdRoute)}
|
||||
isHidden={!isAllowedResource("customresourcedefinitions")}
|
||||
icon={<Icon material="extension"/>}
|
||||
>
|
||||
{routes.get().map((route) => (
|
||||
<SidebarItem
|
||||
key={route.id}
|
||||
id={route.id}
|
||||
text={route.title}
|
||||
url={route.routePath}
|
||||
>
|
||||
{route.subRoutes?.map((subRoute) => (
|
||||
<SidebarItem
|
||||
key={subRoute.id}
|
||||
id={subRoute.id}
|
||||
url={subRoute.routePath}
|
||||
text={subRoute.title}
|
||||
/>
|
||||
))}
|
||||
</SidebarItem>
|
||||
))}
|
||||
{crdStore.isLoading && (
|
||||
<div className="flex justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const CustomResourcesSidebarItem = withInjectables<Dependencies, CustomResourcesSidebarItemProps>(NonInjectedCustomResourcesSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(customResourcesRouteTabsInjectable),
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -3,5 +3,4 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./events";
|
||||
export * from "./event-details";
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { eventRoute, eventsURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface EventsSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedEventsSidebarItem = observer(({ isAllowedResource }: Dependencies & EventsSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="events"
|
||||
text="Events"
|
||||
isActive={isActiveRoute(eventRoute)}
|
||||
isHidden={!isAllowedResource("events")}
|
||||
url={eventsURL()}
|
||||
icon={<Icon material="access_time"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const EventsSidebarItem = withInjectables<Dependencies, EventsSidebarItemProps>(NonInjectedEventsSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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 { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { HelmCharts } from "../+helm-charts";
|
||||
import { HelmReleases } from "../+helm-releases";
|
||||
import { helmChartsURL, helmChartsRoute, releaseURL, releaseRoute } from "../../../common/routes";
|
||||
|
||||
function getRouteTabs() {
|
||||
return computed((): TabLayoutRoute[] => [
|
||||
{
|
||||
title: "Charts",
|
||||
component: HelmCharts,
|
||||
url: helmChartsURL(),
|
||||
routePath: helmChartsRoute.path.toString(),
|
||||
},
|
||||
{
|
||||
title: "Releases",
|
||||
component: HelmReleases,
|
||||
url: releaseURL(),
|
||||
routePath: releaseRoute.path.toString(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const helmRoutesInjectable = getInjectable({
|
||||
instantiate: () => getRouteTabs(),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default helmRoutesInjectable;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import helmRoutesInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface HelmRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedHelmRoute = observer(({ routes }: Dependencies & HelmRouteProps) => (
|
||||
<TabLayout
|
||||
className="Apps"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const HelmRoute = withInjectables<Dependencies, HelmRouteProps>(NonInjectedHelmRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(helmRoutesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { TabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import { helmRoute, helmURL } from "../../../common/routes";
|
||||
import networkRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface HelmSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedHelmSidebarItem = observer(({ routes }: Dependencies & HelmSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="helm"
|
||||
text="Helm"
|
||||
isActive={isActiveRoute(helmRoute)}
|
||||
url={helmURL()}
|
||||
icon={<Icon material="apps"/>}
|
||||
>
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const HelmSidebarItem = withInjectables<Dependencies, HelmSidebarItemProps>(NonInjectedHelmSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(networkRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -3,6 +3,5 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./namespaces";
|
||||
export * from "./namespace-details";
|
||||
export * from "./add-namespace-dialog";
|
||||
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./namespaces.scss";
|
||||
|
||||
import React from "react";
|
||||
import { NamespaceStatus } from "../../../common/k8s-api/endpoints";
|
||||
import { AddNamespaceDialog } from "./add-namespace-dialog";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { Badge } from "../badge";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { NamespaceStore } from "./namespace-store/namespace.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { NamespacesRouteParams } from "../../../common/routes";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import namespaceStoreInjectable from "./namespace-store/namespace-store.injectable";
|
||||
import addNamespaceDialogModelInjectable
|
||||
from "./add-namespace-dialog-model/add-namespace-dialog-model.injectable";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
labels = "labels",
|
||||
age = "age",
|
||||
status = "status",
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<NamespacesRouteParams> {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
namespaceStore: NamespaceStore
|
||||
openAddNamespaceDialog: () => void
|
||||
}
|
||||
|
||||
class NonInjectedNamespaces extends React.Component<Props & Dependencies> {
|
||||
render() {
|
||||
return (
|
||||
<TabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="namespaces"
|
||||
className="Namespaces"
|
||||
store={this.props.namespaceStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: ns => ns.getName(),
|
||||
[columnId.labels]: ns => ns.getLabels(),
|
||||
[columnId.age]: ns => ns.getTimeDiffFromNow(),
|
||||
[columnId.status]: ns => ns.getStatus(),
|
||||
}}
|
||||
searchFilters={[
|
||||
item => item.getSearchFields(),
|
||||
item => item.getStatus(),
|
||||
]}
|
||||
renderHeaderTitle="Namespaces"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Labels", className: "labels scrollable", sortBy: columnId.labels, id: columnId.labels },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||
]}
|
||||
renderTableContents={item => [
|
||||
item.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={item} />,
|
||||
item.getLabels().map(label => <Badge scrollable key={label} label={label}/>),
|
||||
item.getAge(),
|
||||
{ title: item.getStatus(), className: item.getStatus().toLowerCase() },
|
||||
]}
|
||||
addRemoveButtons={{
|
||||
addTooltip: "Add Namespace",
|
||||
onAdd: () => this.props.openAddNamespaceDialog(),
|
||||
}}
|
||||
customizeTableRowProps={item => ({
|
||||
disabled: item.getStatus() === NamespaceStatus.TERMINATING,
|
||||
})}
|
||||
/>
|
||||
<AddNamespaceDialog/>
|
||||
</TabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Namespaces = withInjectables<Dependencies, Props>(
|
||||
NonInjectedNamespaces,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||
openAddNamespaceDialog: di.inject(addNamespaceDialogModelInjectable).open,
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./namespaces.scss";
|
||||
|
||||
import React from "react";
|
||||
import { NamespaceStatus } from "../../../common/k8s-api/endpoints";
|
||||
import { AddNamespaceDialog } from "./add-namespace-dialog";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { Badge } from "../badge";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { NamespaceStore } from "./namespace-store/namespace.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { NamespacesRouteParams } from "../../../common/routes";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import namespaceStoreInjectable from "./namespace-store/namespace-store.injectable";
|
||||
import addNamespaceDialogModelInjectable
|
||||
from "./add-namespace-dialog-model/add-namespace-dialog-model.injectable";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
labels = "labels",
|
||||
age = "age",
|
||||
status = "status",
|
||||
}
|
||||
|
||||
export interface NamespacesRouteProps extends RouteComponentProps<NamespacesRouteParams> {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
namespaceStore: NamespaceStore
|
||||
openAddNamespaceDialog: () => void
|
||||
}
|
||||
|
||||
export const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies & NamespacesRouteProps) => (
|
||||
<TabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="namespaces"
|
||||
className="Namespaces"
|
||||
store={namespaceStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: ns => ns.getName(),
|
||||
[columnId.labels]: ns => ns.getLabels(),
|
||||
[columnId.age]: ns => ns.getTimeDiffFromNow(),
|
||||
[columnId.status]: ns => ns.getStatus(),
|
||||
}}
|
||||
searchFilters={[
|
||||
item => item.getSearchFields(),
|
||||
item => item.getStatus(),
|
||||
]}
|
||||
renderHeaderTitle="Namespaces"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Labels", className: "labels scrollable", sortBy: columnId.labels, id: columnId.labels },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||
]}
|
||||
renderTableContents={item => [
|
||||
item.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={item} />,
|
||||
item.getLabels().map(label => <Badge scrollable key={label} label={label}/>),
|
||||
item.getAge(),
|
||||
{ title: item.getStatus(), className: item.getStatus().toLowerCase() },
|
||||
]}
|
||||
addRemoveButtons={{
|
||||
addTooltip: "Add Namespace",
|
||||
onAdd: openAddNamespaceDialog,
|
||||
}}
|
||||
customizeTableRowProps={item => ({
|
||||
disabled: item.getStatus() === NamespaceStatus.TERMINATING,
|
||||
})}
|
||||
/>
|
||||
<AddNamespaceDialog/>
|
||||
</TabLayout>
|
||||
);
|
||||
|
||||
|
||||
export const NamespacesRoute = withInjectables<Dependencies, NamespacesRouteProps>(NonInjectedNamespacesRoute, {
|
||||
getProps: (di, props) => ({
|
||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||
openAddNamespaceDialog: di.inject(addNamespaceDialogModelInjectable).open,
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { namespacesRoute, namespacesURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface NamespacesSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedNamespacesSidebarItem = observer(({ isAllowedResource }: Dependencies & NamespacesSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="namespaces"
|
||||
text="Namespaces"
|
||||
isActive={isActiveRoute(namespacesRoute)}
|
||||
isHidden={!isAllowedResource("namespaces")}
|
||||
url={namespacesURL()}
|
||||
icon={<Icon material="layers"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const NamespacesSidebarItem = withInjectables<Dependencies, NamespacesSidebarItemProps>(NonInjectedNamespacesSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./network";
|
||||
+21
-17
@@ -2,23 +2,24 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./network.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { Services } from "../+network-services";
|
||||
import { Endpoints } from "../+network-endpoints";
|
||||
import { Ingresses } from "../+network-ingresses";
|
||||
import { NetworkPolicies } from "../+network-policies";
|
||||
import { PortForwards } from "../+network-port-forwards";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import * as routes from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
|
||||
@observer
|
||||
export class Network extends React.Component {
|
||||
static get tabRoutes(): TabLayoutRoute[] {
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
function getRouteTabs({ isAllowedResource }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: TabLayoutRoute[] = [];
|
||||
|
||||
if (isAllowedResource("services")) {
|
||||
@@ -65,11 +66,14 @@ export class Network extends React.Component {
|
||||
});
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout className="Network" tabs={Network.tabRoutes}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const networkRouteTabsInjectable = getInjectable({
|
||||
instantiate: (di) => getRouteTabs({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default networkRouteTabsInjectable;
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./network.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import networkRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface NetworksRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedNetworksRoute = observer(({ routes }: Dependencies & NetworksRouteProps) => (
|
||||
<TabLayout
|
||||
className="Network"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const NetworkRoute = withInjectables<Dependencies, NetworksRouteProps>(NonInjectedNetworksRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(networkRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { TabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import { networkRoute, networkURL } from "../../../common/routes";
|
||||
import networkRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface NetworkSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedNetworkSidebarItem = observer(({ routes }: Dependencies & NetworkSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="networks"
|
||||
text="Network"
|
||||
isActive={isActiveRoute(networkRoute)}
|
||||
isHidden={tabRoutes.length == 0}
|
||||
url={networkURL()}
|
||||
icon={<Icon material="device_hub"/>}
|
||||
>
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const NetworkSidebarItem = withInjectables<Dependencies, NetworkSidebarItemProps>(NonInjectedNetworkSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(networkRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
File renamed without changes.
+1
-1
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./node-details-resources.scss";
|
||||
import "./details-resources.scss";
|
||||
|
||||
import { Table } from "../table/table";
|
||||
import { TableHead } from "../table/table-head";
|
||||
File renamed without changes.
+2
-2
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./node-details.scss";
|
||||
import "./details.scss";
|
||||
|
||||
import React from "react";
|
||||
import upperFirst from "lodash/upperFirst";
|
||||
@@ -21,7 +21,7 @@ import { PodDetailsList } from "../+workloads-pods/pod-details-list";
|
||||
import { KubeObjectMeta } from "../kube-object-meta";
|
||||
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||
import { NodeDetailsResources } from "./node-details-resources";
|
||||
import { NodeDetailsResources } from "./details-resources";
|
||||
import { DrawerTitle } from "../drawer/drawer-title";
|
||||
import { boundMethod, Disposer } from "../../utils";
|
||||
import logger from "../../../common/logger";
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./nodes";
|
||||
export * from "./node-details";
|
||||
@@ -50,7 +50,7 @@ interface UsageArgs {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Nodes extends React.Component<Props> {
|
||||
export class NodesRoute extends React.Component<Props> {
|
||||
@observable.ref metrics: Partial<INodeMetrics> = {};
|
||||
private metricsWatcher = interval(30, async () => this.metrics = await getMetricsForAllNodes());
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { nodesRoute, nodesURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface NodeSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedNodeSidebarItem = observer(({ isAllowedResource }: Dependencies & NodeSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="nodes"
|
||||
text="Nodes"
|
||||
isActive={isActiveRoute(nodesRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={nodesURL()}
|
||||
icon={<Icon svg="nodes"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const NodesSidebarItem = withInjectables<Dependencies, NodeSidebarItemProps>(NonInjectedNodeSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./storage";
|
||||
+21
-17
@@ -2,21 +2,22 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./storage.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { PersistentVolumes } from "../+storage-volumes";
|
||||
import { StorageClasses } from "../+storage-classes";
|
||||
import { PersistentVolumeClaims } from "../+storage-volume-claims";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import * as routes from "../../../common/routes";
|
||||
|
||||
@observer
|
||||
export class Storage extends React.Component {
|
||||
static get tabRoutes() {
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
function getRouteTabs({ isAllowedResource }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: TabLayoutRoute[] = [];
|
||||
|
||||
if (isAllowedResource("persistentvolumeclaims")) {
|
||||
@@ -47,11 +48,14 @@ export class Storage extends React.Component {
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout className="Storage" tabs={Storage.tabRoutes}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const storageRouteTabsInjectable = getInjectable({
|
||||
instantiate: (di) => getRouteTabs({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default storageRouteTabsInjectable;
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./storage.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import storageRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface StorageRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedStorageRoute = observer(({ routes }: Dependencies & StorageRouteProps) => (
|
||||
<TabLayout
|
||||
className="Storage"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const StorageRoute = withInjectables<Dependencies, StorageRouteProps>(NonInjectedStorageRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(storageRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { storageRoute, storageURL } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { TabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import storageRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface StorageSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedStorageSidebarItem = observer(({ routes }: Dependencies & StorageSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="storage"
|
||||
text="Storage"
|
||||
isActive={isActiveRoute(storageRoute)}
|
||||
isHidden={tabRoutes.length == 0}
|
||||
url={storageURL()}
|
||||
icon={<Icon svg="storage"/>}
|
||||
>
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const StorageSidebarItem = withInjectables<Dependencies, StorageSidebarItemProps>(NonInjectedStorageSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(storageRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./user-management";
|
||||
+33
-29
@@ -2,28 +2,29 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./user-management.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { PodSecurityPolicies } from "../+pod-security-policies";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import * as routes from "../../../common/routes";
|
||||
import { PodSecurityPolicies } from "../+pod-security-policies";
|
||||
import { ClusterRoleBindings } from "./+cluster-role-bindings";
|
||||
import { ServiceAccounts } from "./+service-accounts";
|
||||
import { Roles } from "./+roles";
|
||||
import { RoleBindings } from "./+role-bindings";
|
||||
import { ClusterRoles } from "./+cluster-roles";
|
||||
import { RoleBindings } from "./+role-bindings";
|
||||
import { Roles } from "./+roles";
|
||||
import { ServiceAccounts } from "./+service-accounts";
|
||||
|
||||
@observer
|
||||
export class UserManagement extends React.Component {
|
||||
static get tabRoutes() {
|
||||
const tabRoutes: TabLayoutRoute[] = [];
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
function getRouteTabs({ isAllowedResource }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: TabLayoutRoute[] = [];
|
||||
|
||||
if (isAllowedResource("serviceaccounts")) {
|
||||
tabRoutes.push({
|
||||
tabs.push({
|
||||
title: "Service Accounts",
|
||||
component: ServiceAccounts,
|
||||
url: routes.serviceAccountsURL(),
|
||||
@@ -32,7 +33,7 @@ export class UserManagement extends React.Component {
|
||||
}
|
||||
|
||||
if (isAllowedResource("clusterroles")) {
|
||||
tabRoutes.push({
|
||||
tabs.push({
|
||||
title: "Cluster Roles",
|
||||
component: ClusterRoles,
|
||||
url: routes.clusterRolesURL(),
|
||||
@@ -41,7 +42,7 @@ export class UserManagement extends React.Component {
|
||||
}
|
||||
|
||||
if (isAllowedResource("roles")) {
|
||||
tabRoutes.push({
|
||||
tabs.push({
|
||||
title: "Roles",
|
||||
component: Roles,
|
||||
url: routes.rolesURL(),
|
||||
@@ -50,7 +51,7 @@ export class UserManagement extends React.Component {
|
||||
}
|
||||
|
||||
if (isAllowedResource("clusterrolebindings")) {
|
||||
tabRoutes.push({
|
||||
tabs.push({
|
||||
title: "Cluster Role Bindings",
|
||||
component: ClusterRoleBindings,
|
||||
url: routes.clusterRoleBindingsURL(),
|
||||
@@ -59,7 +60,7 @@ export class UserManagement extends React.Component {
|
||||
}
|
||||
|
||||
if (isAllowedResource("rolebindings")) {
|
||||
tabRoutes.push({
|
||||
tabs.push({
|
||||
title: "Role Bindings",
|
||||
component: RoleBindings,
|
||||
url: routes.roleBindingsURL(),
|
||||
@@ -68,7 +69,7 @@ export class UserManagement extends React.Component {
|
||||
}
|
||||
|
||||
if (isAllowedResource("podsecuritypolicies")) {
|
||||
tabRoutes.push({
|
||||
tabs.push({
|
||||
title: "Pod Security Policies",
|
||||
component: PodSecurityPolicies,
|
||||
url: routes.podSecurityPoliciesURL(),
|
||||
@@ -76,12 +77,15 @@ export class UserManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
return tabRoutes;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout className="UserManagement" tabs={UserManagement.tabRoutes}/>
|
||||
);
|
||||
}
|
||||
return tabs;
|
||||
});
|
||||
}
|
||||
|
||||
const userManagementRouteTabsInjectable = getInjectable({
|
||||
instantiate: (di) => getRouteTabs({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default userManagementRouteTabsInjectable;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./user-management.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import userManagementRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedUserManagementRoute = observer(({ routes }: Dependencies) => (
|
||||
<TabLayout
|
||||
className="UserManagement"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const UserManagementRoute = withInjectables<Dependencies>(NonInjectedUserManagementRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(userManagementRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { usersManagementRoute, usersManagementURL } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { TabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import userManagementRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface UserManagementSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedUserManagementSidebarItem = observer(({ routes }: Dependencies & UserManagementSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="users"
|
||||
text="Access Control"
|
||||
isActive={isActiveRoute(usersManagementRoute)}
|
||||
isHidden={tabRoutes.length === 0}
|
||||
url={usersManagementURL()}
|
||||
icon={<Icon material="security"/>}
|
||||
>
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const UserManagementSidebarItem = withInjectables<Dependencies, UserManagementSidebarItemProps>(NonInjectedUserManagementSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(userManagementRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -9,73 +9,49 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { OverviewWorkloadStatus } from "./overview-workload-status";
|
||||
import { Link } from "react-router-dom";
|
||||
import { workloadStores } from "../+workloads";
|
||||
import type { NamespaceStore } from "../+namespaces/namespace-store/namespace.store";
|
||||
import type { KubeResource } from "../../../common/rbac";
|
||||
import { ResourceNames } from "../../utils/rbac";
|
||||
import { boundMethod } from "../../utils";
|
||||
import { workloadURL } from "../../../common/routes";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import workloadsInjectable from "./workloads.injectable";
|
||||
|
||||
const resources: KubeResource[] = [
|
||||
"pods",
|
||||
"deployments",
|
||||
"statefulsets",
|
||||
"daemonsets",
|
||||
"replicasets",
|
||||
"jobs",
|
||||
"cronjobs",
|
||||
];
|
||||
export interface OverviewStatusesProps {}
|
||||
|
||||
interface Workload {
|
||||
resource: KubeResource;
|
||||
amountOfItems: number;
|
||||
href: string;
|
||||
status: Record<string, number>;
|
||||
title: string
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
namespaceStore: NamespaceStore
|
||||
workloads: IComputedValue<Workload[]>;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedOverviewStatuses extends React.Component<Dependencies> {
|
||||
@boundMethod
|
||||
renderWorkload(resource: KubeResource): React.ReactElement {
|
||||
const store = workloadStores.get(resource);
|
||||
const NonInjectedOverviewStatuses = observer(
|
||||
({ workloads }: Dependencies & OverviewStatusesProps) => (
|
||||
<div className="OverviewStatuses">
|
||||
<div className="workloads">
|
||||
{workloads.get()
|
||||
.map(({ resource, title, href, status, amountOfItems }) => (
|
||||
<div className="workload" key={resource}>
|
||||
<div className="title">
|
||||
<Link to={href}>
|
||||
{title} ({amountOfItems})
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
if (!store) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = store.getAllByNs(this.props.namespaceStore.contextNamespaces);
|
||||
|
||||
return (
|
||||
<div className="workload" key={resource}>
|
||||
<div className="title">
|
||||
<Link to={workloadURL[resource]()}>{ResourceNames[resource]} ({items.length})</Link>
|
||||
</div>
|
||||
<OverviewWorkloadStatus status={store.getStatuses(items)} />
|
||||
<OverviewWorkloadStatus status={status} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const workloads = resources
|
||||
.filter(isAllowedResource)
|
||||
.map(this.renderWorkload);
|
||||
|
||||
return (
|
||||
<div className="OverviewStatuses">
|
||||
<div className="workloads">
|
||||
{workloads}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const OverviewStatuses = withInjectables<Dependencies>(
|
||||
NonInjectedOverviewStatuses,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||
}),
|
||||
},
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
export const OverviewStatuses = withInjectables<Dependencies, OverviewStatusesProps>(NonInjectedOverviewStatuses, {
|
||||
getProps: (di, props) => ({
|
||||
workloads: di.inject(workloadsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 type { KubeResource } from "../../../common/rbac";
|
||||
import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { deploymentStore } from "../+workloads-deployments/deployments.store";
|
||||
import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store";
|
||||
import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store";
|
||||
import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
|
||||
import { jobStore } from "../+workloads-jobs/job.store";
|
||||
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable";
|
||||
import { workloads } from "./workloads";
|
||||
|
||||
const workloadsInjectable = getInjectable({
|
||||
instantiate: (di) =>
|
||||
workloads({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||
|
||||
workloadStores: new Map<KubeResource, KubeObjectStore<KubeObject>>([
|
||||
["pods", podsStore],
|
||||
["deployments", deploymentStore],
|
||||
["daemonsets", daemonSetStore],
|
||||
["statefulsets", statefulSetStore],
|
||||
["replicasets", replicaSetStore],
|
||||
["jobs", jobStore],
|
||||
["cronjobs", cronJobStore],
|
||||
]),
|
||||
}),
|
||||
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default workloadsInjectable;
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { computed } from "mobx";
|
||||
import type { KubeResource } from "../../../common/rbac";
|
||||
import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { workloadURL } from "../../../common/routes";
|
||||
import { ResourceNames } from "../../utils/rbac";
|
||||
import type { NamespaceStore } from "../+namespaces/namespace-store/namespace.store";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
workloadStores: Map<KubeResource, KubeObjectStore<KubeObject>>;
|
||||
isAllowedResource: IsAllowedResource;
|
||||
namespaceStore: NamespaceStore;
|
||||
}
|
||||
|
||||
export const workloads = ({
|
||||
workloadStores,
|
||||
isAllowedResource,
|
||||
namespaceStore,
|
||||
}: Dependencies) =>
|
||||
computed(() =>
|
||||
[...workloadStores.entries()]
|
||||
.filter(([resource]) => isAllowedResource(resource))
|
||||
.map(([resource, store]) => {
|
||||
const items = store.getAllByNs(namespaceStore.contextNamespaces);
|
||||
|
||||
return {
|
||||
resource,
|
||||
href: workloadURL[resource](),
|
||||
amountOfItems: items.length,
|
||||
status: store.getStatuses(items),
|
||||
title: ResourceNames[resource],
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./workloads";
|
||||
export * from "./workloads.stores";
|
||||
+21
-15
@@ -2,11 +2,9 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./workloads.scss";
|
||||
|
||||
import React from "react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { WorkloadsOverview } from "../+workloads-overview/overview";
|
||||
import { Pods } from "../+workloads-pods";
|
||||
import { Deployments } from "../+workloads-deployments";
|
||||
@@ -14,12 +12,17 @@ import { DaemonSets } from "../+workloads-daemonsets";
|
||||
import { StatefulSets } from "../+workloads-statefulsets";
|
||||
import { Jobs } from "../+workloads-jobs";
|
||||
import { CronJobs } from "../+workloads-cronjobs";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { ReplicaSets } from "../+workloads-replicasets";
|
||||
import * as routes from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
|
||||
export class Workloads extends React.Component {
|
||||
static get tabRoutes(): TabLayoutRoute[] {
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
function getRouteTabs({ isAllowedResource }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: TabLayoutRoute[] = [
|
||||
{
|
||||
title: "Overview",
|
||||
@@ -93,11 +96,14 @@ export class Workloads extends React.Component {
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabLayout className="Workloads" tabs={Workloads.tabRoutes}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const workloadsRouteTabsInjectable = getInjectable({
|
||||
instantiate: (di) => getRouteTabs({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
});
|
||||
|
||||
export default workloadsRouteTabsInjectable;
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./workloads.scss";
|
||||
|
||||
import React from "react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import workloadsRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface WorkloadsRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedWorkloadsRoute = observer(({ routes }: Dependencies & WorkloadsRouteProps) => (
|
||||
<TabLayout
|
||||
className="Workloads"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const WorkloadsRoute = withInjectables<Dependencies, WorkloadsRouteProps>(NonInjectedWorkloadsRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(workloadsRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { workloadsRoute, workloadsURL } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { TabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import workloadsRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface WorkloadSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedWorkloadsSidebarItem = observer(({ routes }: Dependencies & WorkloadSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="workloads"
|
||||
text="Workloads"
|
||||
isActive={isActiveRoute(workloadsRoute)}
|
||||
isHidden={tabRoutes.length == 0}
|
||||
url={workloadsURL()}
|
||||
icon={<Icon svg="workloads"/>}
|
||||
>
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const WorkloadsSidebarItem = withInjectables<Dependencies, WorkloadSidebarItemProps>(NonInjectedWorkloadsSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(workloadsRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { deploymentStore } from "../+workloads-deployments/deployments.store";
|
||||
import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store";
|
||||
import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store";
|
||||
import { jobStore } from "../+workloads-jobs/job.store";
|
||||
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||
import type { KubeResource } from "../../../common/rbac";
|
||||
import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
|
||||
export const workloadStores = new Map<KubeResource, KubeObjectStore<KubeObject>>([
|
||||
["pods", podsStore],
|
||||
["deployments", deploymentStore],
|
||||
["daemonsets", daemonSetStore],
|
||||
["statefulsets", statefulSetStore],
|
||||
["replicasets", replicaSetStore],
|
||||
["jobs", jobStore],
|
||||
["cronjobs", cronJobStore],
|
||||
]);
|
||||
@@ -27,7 +27,7 @@ import type { IReleaseCreatePayload, IReleaseUpdateDetails } from "../../../../c
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import installChartTabStoreInjectable from "./store.injectable";
|
||||
import dockStoreInjectable from "../dock/store.injectable";
|
||||
import createReleaseInjectable from "../../+apps-releases/create-release/create-release.injectable";
|
||||
import createReleaseInjectable from "../../+helm-releases/create-release/create-release.injectable";
|
||||
import { Notifications } from "../../notifications";
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -15,13 +15,13 @@ import type { UpgradeChartTabStore } from "./store";
|
||||
import { Spinner } from "../../spinner";
|
||||
import { Badge } from "../../badge";
|
||||
import { EditorPanel } from "../editor-panel";
|
||||
import { helmChartStore, IChartVersion } from "../../+apps-helm-charts/helm-chart.store";
|
||||
import { helmChartStore, IChartVersion } from "../../+helm-charts/helm-chart.store";
|
||||
import type { HelmRelease, IReleaseUpdateDetails, IReleaseUpdatePayload } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import { Select, SelectOption } from "../../select";
|
||||
import { IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react";
|
||||
import upgradeChartTabStoreInjectable from "./store.injectable";
|
||||
import updateReleaseInjectable from "../../+apps-releases/update-release/update-release.injectable";
|
||||
import releasesInjectable from "../../+apps-releases/releases.injectable";
|
||||
import updateReleaseInjectable from "../../+helm-releases/update-release/update-release.injectable";
|
||||
import releasesInjectable from "../../+helm-releases/releases.injectable";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
||||
@@ -7,99 +7,34 @@ import styles from "./sidebar.module.scss";
|
||||
import type { TabLayoutRoute } from "./tab-layout";
|
||||
|
||||
import React from "react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { cssNames, Disposer } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { Workloads } from "../+workloads";
|
||||
import { UserManagement } from "../+user-management";
|
||||
import { Storage } from "../+storage";
|
||||
import { Network } from "../+network";
|
||||
import { crdStore } from "../+custom-resources/crd.store";
|
||||
import { CustomResources } from "../+custom-resources/custom-resources";
|
||||
import { observer } from "mobx-react";
|
||||
import { cssNames } from "../../utils";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { Spinner } from "../spinner";
|
||||
import { ClusterPageMenuRegistration, ClusterPageMenuRegistry, ClusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries";
|
||||
import { SidebarItem } from "./sidebar-item";
|
||||
import { Apps } from "../+apps";
|
||||
import * as routes from "../../../common/routes";
|
||||
import { Config } from "../+config";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import { SidebarCluster } from "./sidebar-cluster";
|
||||
import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import kubeWatchApiInjectable
|
||||
from "../../kube-watch-api/kube-watch-api.injectable";
|
||||
import { TabRoutesSidebarItems } from "./tab-routes-sidebar-items";
|
||||
import { ConfigSidebarItem } from "../+config/sidebar-item";
|
||||
import { ClusterSidebarItem } from "../+cluster/sidebar-item";
|
||||
import { NodesSidebarItem } from "../+nodes/sidebar-item";
|
||||
import { WorkloadsSidebarItem } from "../+workloads/sidebar-item";
|
||||
import { NetworkSidebarItem } from "../+network/sidebar-item";
|
||||
import { StorageSidebarItem } from "../+storage/sidebar-item";
|
||||
import { NamespacesSidebarItem } from "../+namespaces/sidebar-item";
|
||||
import { EventsSidebarItem } from "../+events/sidebar-item";
|
||||
import { HelmSidebarItem } from "../+helm/sidebar-item";
|
||||
import { UserManagementSidebarItem } from "../+user-management/sidebar-item";
|
||||
import { CustomResourcesSidebarItem } from "../+custom-resources/sidebar-item";
|
||||
|
||||
interface Props {
|
||||
interface SidebarProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
subscribeStores: (stores: KubeObjectStore<KubeObject>[]) => Disposer
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedSidebar extends React.Component<Props & Dependencies> {
|
||||
export class Sidebar extends React.Component<SidebarProps> {
|
||||
static displayName = "Sidebar";
|
||||
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this, [
|
||||
this.props.subscribeStores([
|
||||
crdStore,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
renderCustomResources() {
|
||||
if (crdStore.isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Spinner/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return Object.entries(crdStore.groups).map(([group, crds]) => {
|
||||
const id = `crd-group:${group}`;
|
||||
const crdGroupsPageUrl = routes.crdURL({ query: { groups: group }});
|
||||
|
||||
return (
|
||||
<SidebarItem key={id} id={id} text={group} url={crdGroupsPageUrl}>
|
||||
{crds.map((crd) => (
|
||||
<SidebarItem
|
||||
key={crd.getResourceApiBase()}
|
||||
id={`crd-resource:${crd.getResourceApiBase()}`}
|
||||
url={crd.getResourceUrl()}
|
||||
text={crd.getResourceKind()}
|
||||
/>
|
||||
))}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderTreeFromTabRoutes(tabRoutes: TabLayoutRoute[] = []): React.ReactNode {
|
||||
if (!tabRoutes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tabRoutes.map(({ title, routePath, url = routePath, exact = true }) => {
|
||||
const subMenuItemId = `tab-route-item-${url}`;
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
key={subMenuItemId}
|
||||
id={subMenuItemId}
|
||||
url={url}
|
||||
text={title}
|
||||
isActive={isActiveRoute({ path: routePath, exact })}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getTabLayoutRoutes(menu: ClusterPageMenuRegistration): TabLayoutRoute[] {
|
||||
if (!menu.id) {
|
||||
return [];
|
||||
@@ -169,7 +104,7 @@ class NonInjectedSidebar extends React.Component<Props & Dependencies> {
|
||||
text={menuItem.title}
|
||||
icon={<menuItem.components.Icon/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(tabRoutes)}
|
||||
<TabRoutesSidebarItems routes={tabRoutes} />
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
@@ -186,122 +121,20 @@ class NonInjectedSidebar extends React.Component<Props & Dependencies> {
|
||||
<div className={cssNames("flex flex-col", className)} data-testid="cluster-sidebar">
|
||||
<SidebarCluster clusterEntity={this.clusterEntity}/>
|
||||
<div className={styles.sidebarNav}>
|
||||
<SidebarItem
|
||||
id="cluster"
|
||||
text="Cluster"
|
||||
isActive={isActiveRoute(routes.clusterRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={routes.clusterURL()}
|
||||
icon={<Icon svg="kube"/>}
|
||||
/>
|
||||
<SidebarItem
|
||||
id="nodes"
|
||||
text="Nodes"
|
||||
isActive={isActiveRoute(routes.nodesRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={routes.nodesURL()}
|
||||
icon={<Icon svg="nodes"/>}
|
||||
/>
|
||||
<SidebarItem
|
||||
id="workloads"
|
||||
text="Workloads"
|
||||
isActive={isActiveRoute(routes.workloadsRoute)}
|
||||
isHidden={Workloads.tabRoutes.length == 0}
|
||||
url={routes.workloadsURL()}
|
||||
icon={<Icon svg="workloads"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Workloads.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="config"
|
||||
text="Configuration"
|
||||
isActive={isActiveRoute(routes.configRoute)}
|
||||
isHidden={Config.tabRoutes.length == 0}
|
||||
url={routes.configURL()}
|
||||
icon={<Icon material="list"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Config.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="networks"
|
||||
text="Network"
|
||||
isActive={isActiveRoute(routes.networkRoute)}
|
||||
isHidden={Network.tabRoutes.length == 0}
|
||||
url={routes.networkURL()}
|
||||
icon={<Icon material="device_hub"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Network.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="storage"
|
||||
text="Storage"
|
||||
isActive={isActiveRoute(routes.storageRoute)}
|
||||
isHidden={Storage.tabRoutes.length == 0}
|
||||
url={routes.storageURL()}
|
||||
icon={<Icon svg="storage"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Storage.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="namespaces"
|
||||
text="Namespaces"
|
||||
isActive={isActiveRoute(routes.namespacesRoute)}
|
||||
isHidden={!isAllowedResource("namespaces")}
|
||||
url={routes.namespacesURL()}
|
||||
icon={<Icon material="layers"/>}
|
||||
/>
|
||||
<SidebarItem
|
||||
id="events"
|
||||
text="Events"
|
||||
isActive={isActiveRoute(routes.eventRoute)}
|
||||
isHidden={!isAllowedResource("events")}
|
||||
url={routes.eventsURL()}
|
||||
icon={<Icon material="access_time"/>}
|
||||
/>
|
||||
<SidebarItem
|
||||
id="apps"
|
||||
text="Apps" // helm charts
|
||||
isActive={isActiveRoute(routes.appsRoute)}
|
||||
url={routes.appsURL()}
|
||||
icon={<Icon material="apps"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(Apps.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="users"
|
||||
text="Access Control"
|
||||
isActive={isActiveRoute(routes.usersManagementRoute)}
|
||||
isHidden={UserManagement.tabRoutes.length === 0}
|
||||
url={routes.usersManagementURL()}
|
||||
icon={<Icon material="security"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(UserManagement.tabRoutes)}
|
||||
</SidebarItem>
|
||||
<SidebarItem
|
||||
id="custom-resources"
|
||||
text="Custom Resources"
|
||||
url={routes.crdURL()}
|
||||
isActive={isActiveRoute(routes.crdRoute)}
|
||||
isHidden={!isAllowedResource("customresourcedefinitions")}
|
||||
icon={<Icon material="extension"/>}
|
||||
>
|
||||
{this.renderTreeFromTabRoutes(CustomResources.tabRoutes)}
|
||||
{this.renderCustomResources()}
|
||||
</SidebarItem>
|
||||
<ClusterSidebarItem />
|
||||
<NodesSidebarItem />
|
||||
<WorkloadsSidebarItem />
|
||||
<ConfigSidebarItem />
|
||||
<NetworkSidebarItem />
|
||||
<StorageSidebarItem />
|
||||
<NamespacesSidebarItem />
|
||||
<EventsSidebarItem />
|
||||
<HelmSidebarItem />
|
||||
<UserManagementSidebarItem />
|
||||
<CustomResourcesSidebarItem />
|
||||
{this.renderRegisteredMenus()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Sidebar = withInjectables<Dependencies, Props>(
|
||||
NonInjectedSidebar,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
subscribeStores: di.inject(kubeWatchApiInjectable).subscribeStores,
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { SidebarItem } from "./sidebar-item";
|
||||
import type { TabLayoutRoute } from "./tab-layout";
|
||||
|
||||
export interface SidebarTreeProps {
|
||||
routes: TabLayoutRoute[];
|
||||
}
|
||||
|
||||
function withId(src: TabLayoutRoute) {
|
||||
return {
|
||||
...src,
|
||||
id: `tab-route-item-${src.url ?? src.routePath}`,
|
||||
};
|
||||
}
|
||||
|
||||
export const TabRoutesSidebarItems = ({ routes }: SidebarTreeProps) => (
|
||||
<>
|
||||
{
|
||||
routes
|
||||
.map(withId)
|
||||
.map(({ title, routePath, url = routePath, exact = true, id }) => (
|
||||
<SidebarItem
|
||||
key={id}
|
||||
id={id}
|
||||
url={url}
|
||||
text={title}
|
||||
isActive={isActiveRoute({ path: routePath, exact })} />
|
||||
))
|
||||
}
|
||||
</>
|
||||
);
|
||||
@@ -3,17 +3,16 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import { makeObservable, computed } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { Redirect, Route, Router, Switch } from "react-router";
|
||||
import { UserManagement } from "../../components/+user-management/user-management";
|
||||
import { UserManagementRoute } from "../../components/+user-management/route";
|
||||
import { ConfirmDialog } from "../../components/confirm-dialog";
|
||||
import { ClusterOverview } from "../../components/+cluster/cluster-overview";
|
||||
import { Events } from "../../components/+events/events";
|
||||
import { DeploymentScaleDialog } from "../../components/+workloads-deployments/deployment-scale-dialog";
|
||||
import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog";
|
||||
import { CustomResources } from "../../components/+custom-resources/custom-resources";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { CustomResourcesRoute } from "../../components/+custom-resources/route";
|
||||
import { ClusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries/page-registry";
|
||||
import { ClusterPageMenuRegistration, ClusterPageMenuRegistry } from "../../../extensions/registries";
|
||||
import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/statefulset-scale-dialog";
|
||||
@@ -28,13 +27,12 @@ import { KubeObjectDetails } from "../../components/kube-object-details";
|
||||
import { KubeConfigDialog } from "../../components/kubeconfig-dialog";
|
||||
import { Sidebar } from "../../components/layout/sidebar";
|
||||
import { Dock } from "../../components/dock";
|
||||
import { Apps } from "../../components/+apps";
|
||||
import { Namespaces } from "../../components/+namespaces";
|
||||
import { Network } from "../../components/+network";
|
||||
import { Nodes } from "../../components/+nodes";
|
||||
import { Workloads } from "../../components/+workloads";
|
||||
import { Config } from "../../components/+config";
|
||||
import { Storage } from "../../components/+storage";
|
||||
import { NamespacesRoute } from "../../components/+namespaces/route";
|
||||
import { NetworkRoute } from "../../components/+network/route";
|
||||
import { NodesRoute } from "../../components/+nodes/route";
|
||||
import { WorkloadsRoute } from "../../components/+workloads/route";
|
||||
import { ConfigRoute } from "../../components/+config/route";
|
||||
import { StorageRoute } from "../../components/+storage/route";
|
||||
import { watchHistoryState } from "../../remote-helpers/history-updater";
|
||||
import { PortForwardDialog } from "../../port-forward";
|
||||
import { DeleteClusterDialog } from "../../components/delete-cluster-dialog";
|
||||
@@ -42,19 +40,24 @@ import type { NamespaceStore } from "../../components/+namespaces/namespace-stor
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import namespaceStoreInjectable from "../../components/+namespaces/namespace-store/namespace-store.injectable";
|
||||
import type { ClusterId } from "../../../common/cluster-types";
|
||||
import hostedClusterInjectable from "../../../common/cluster-store/hosted-cluster/hosted-cluster.injectable";
|
||||
import hostedClusterInjectable from "../../../common/cluster-store/hosted-cluster.injectable";
|
||||
import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type { Disposer } from "../../../common/utils";
|
||||
import kubeWatchApiInjectable from "../../kube-watch-api/kube-watch-api.injectable";
|
||||
import historyInjectable from "../../navigation/history.injectable";
|
||||
import type { History } from "history";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { HelmRoute } from "../../components/+helm/route";
|
||||
import type { KubeResource } from "../../../common/rbac";
|
||||
|
||||
interface Dependencies {
|
||||
history: History,
|
||||
namespaceStore: NamespaceStore
|
||||
hostedClusterId: ClusterId
|
||||
subscribeStores: (stores: KubeObjectStore<KubeObject>[]) => Disposer
|
||||
history: History;
|
||||
namespaceStore: NamespaceStore;
|
||||
hostedClusterId: ClusterId;
|
||||
subscribeStores: (stores: KubeObjectStore<KubeObject>[]) => Disposer;
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
@observer
|
||||
@@ -75,7 +78,13 @@ class NonInjectedClusterFrame extends React.Component<Dependencies> {
|
||||
]);
|
||||
}
|
||||
|
||||
@observable startUrl = isAllowedResource(["events", "nodes", "pods"]) ? routes.clusterURL() : routes.workloadsURL();
|
||||
@computed get startUrl() {
|
||||
const resources : KubeResource[] = ["events", "nodes", "pods"];
|
||||
|
||||
return resources.every(x => this.props.isAllowedResource(x))
|
||||
? routes.clusterURL()
|
||||
: routes.workloadsURL();
|
||||
}
|
||||
|
||||
getTabLayoutRoutes(menuItem: ClusterPageMenuRegistration) {
|
||||
const routes: TabLayoutRoute[] = [];
|
||||
@@ -138,18 +147,17 @@ class NonInjectedClusterFrame extends React.Component<Dependencies> {
|
||||
<ErrorBoundary>
|
||||
<MainLayout sidebar={<Sidebar />} footer={<Dock />}>
|
||||
<Switch>
|
||||
|
||||
<Route component={ClusterOverview} {...routes.clusterRoute}/>
|
||||
<Route component={Nodes} {...routes.nodesRoute}/>
|
||||
<Route component={Workloads} {...routes.workloadsRoute}/>
|
||||
<Route component={Config} {...routes.configRoute}/>
|
||||
<Route component={Network} {...routes.networkRoute}/>
|
||||
<Route component={Storage} {...routes.storageRoute}/>
|
||||
<Route component={Namespaces} {...routes.namespacesRoute}/>
|
||||
<Route component={NodesRoute} {...routes.nodesRoute}/>
|
||||
<Route component={WorkloadsRoute} {...routes.workloadsRoute}/>
|
||||
<Route component={ConfigRoute} {...routes.configRoute}/>
|
||||
<Route component={NetworkRoute} {...routes.networkRoute}/>
|
||||
<Route component={StorageRoute} {...routes.storageRoute}/>
|
||||
<Route component={NamespacesRoute} {...routes.namespacesRoute}/>
|
||||
<Route component={Events} {...routes.eventRoute}/>
|
||||
<Route component={CustomResources} {...routes.crdRoute}/>
|
||||
<Route component={UserManagement} {...routes.usersManagementRoute}/>
|
||||
<Route component={Apps} {...routes.appsRoute}/>
|
||||
<Route component={CustomResourcesRoute} {...routes.crdRoute}/>
|
||||
<Route component={UserManagementRoute} {...routes.usersManagementRoute}/>
|
||||
<Route component={HelmRoute} {...routes.helmRoute}/>
|
||||
{this.renderExtensionTabLayoutRoutes()}
|
||||
{this.renderExtensionRoutes()}
|
||||
<Redirect exact from="/" to={this.startUrl}/>
|
||||
@@ -185,5 +193,6 @@ export const ClusterFrame = withInjectables<Dependencies>(NonInjectedClusterFram
|
||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||
hostedClusterId: di.inject(hostedClusterInjectable).id,
|
||||
subscribeStores: di.inject(kubeWatchApiInjectable).subscribeStores,
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
});
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { initClusterFrame } from "./init-cluster-frame";
|
||||
import extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../../api/catalog-entity-registry/catalog-entity-registry.injectable";
|
||||
import frameRoutingIdInjectable from "./frame-routing-id/frame-routing-id.injectable";
|
||||
import hostedClusterInjectable from "../../../../common/cluster-store/hosted-cluster/hosted-cluster.injectable";
|
||||
import hostedClusterInjectable from "../../../../common/cluster-store/hosted-cluster.injectable";
|
||||
import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
|
||||
import clusterFrameContextInjectable from "../../../cluster-frame-context/cluster-frame-context.injectable";
|
||||
|
||||
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user