diff --git a/src/common/rbac.ts b/src/common/rbac.ts index 4a2ea69a73..e4a20ed655 100644 --- a/src/common/rbac.ts +++ b/src/common/rbac.ts @@ -19,7 +19,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - export type KubeResource = "namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | diff --git a/src/main/cluster.ts b/src/main/cluster.ts index a68d92e222..f5feaf8f27 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -35,6 +35,7 @@ import { ExtendedObservableMap, toJS } from "../common/utils"; import { getClusterResources } from "./utils/api-resources"; import { asyncThrottle } from "../common/utils/async-throttle"; import pLimit from "p-limit"; +import { createKubeApiBase } from "../renderer/api/kube-api-parse"; export enum ClusterStatus { AccessGranted = 2, @@ -527,17 +528,20 @@ export class Cluster implements ClusterModel, ClusterState { for (const [group, versions] of groups) { for (const [version, resources] of versions) { - for (const resource of resources.keys()) { - const canList = await this.canI({ + for (const [resource, apiInfo] of resources) { + const attr: V1ResourceAttributes = { resource, version, group, - namespace, verb: "list", - }); + }; - if (canList) { - isAllowed.add(resource); + if (apiInfo.namespaced) { + attr.namespace = namespace; + } + + if (await this.canI(attr)) { + isAllowed.add(createKubeApiBase(apiInfo)); } } } diff --git a/src/renderer/api/allowed-resources.ts b/src/renderer/api/allowed-resources.ts index 8758f576ab..50c9676103 100644 --- a/src/renderer/api/allowed-resources.ts +++ b/src/renderer/api/allowed-resources.ts @@ -22,16 +22,19 @@ import { action, makeObservable, observable, reaction } from "mobx"; import type { ClusterId } from "../../common/cluster-store"; import { ClusterResourceIsAllowedChannel, ClusterGetResourcesChannel, requestMain } from "../../common/ipc"; -import { Disposer, Singleton } from "../utils"; +import { Disposer, Singleton, toJS } from "../utils"; import type { ApiResourceMap } from "../../main/utils/api-resources"; import { ObservableTimer } from "../../common/utils/observable-timer"; import { Notifications } from "../components/notifications"; type NamespaceName = string; -type ResourceName = string; + +export interface KubeObject { + apiBase: string; +} export class AllowedResources extends Singleton { - protected allowedResources = observable.set(); + protected allowedResources = observable.set(); @observable public resources: ApiResourceMap; /** @@ -51,6 +54,7 @@ export class AllowedResources extends Singleton { async init() { try { this.resources = await requestMain(ClusterGetResourcesChannel, this.clusterId); + console.log(toJS(this.resources)); } catch (error) { console.error("[ALLOWED-RESOURCES]: failed to initialize resources", error); Notifications.error("Failed to initialize resources"); @@ -60,7 +64,7 @@ export class AllowedResources extends Singleton { this.loaded = true; this.disposer = reaction( - () => [this.timer.tickCount, this.getNamespaces()] as const, + () => [this.timer.tickCount, this.getNamespaces()] as const, ([, namespaces]) => this.refresh(namespaces), ); } @@ -77,42 +81,42 @@ export class AllowedResources extends Singleton { /** * Get the permissive list permissions of `name` over `namespaces` - * @param name The name of the resource + * @param obj The name of the resource * @param namespaces The list of namespaces to check (should be `NamepaceSelectFilter` selected ones) * @returns `true` if the resource exists; is cluster scoped and can be listed, or is namespaced and can be listed in at least one of the namespaces */ - isAllowed(name: ResourceName): boolean { - return this.allowedResources.has(name); + isAllowed(obj: KubeObject): boolean { + return this.allowedResources.has(obj.apiBase); } } /** * Get list permissions for a single resource - * @param name The name of the resource to check if it is allowed to be listed + * @param obj The name of the resource to check if it is allowed to be listed * @returns `true` if the resource exists on the cluster and the cluster has list permissions for that resource */ -export function isAllowedResource(name: ResourceName) { - return AllowedResources.getInstance().isAllowed(name); +export function isAllowedResource(obj: KubeObject) { + return AllowedResources.getInstance().isAllowed(obj); } /** * Get list permissions for several resources - * @param names Several names of resources + * @param objs Several names of resources * @returns `true` iff `∀ name ∈ names : isAllowedResource(name)` */ -export function isAllowedResources(...names: ResourceName[]) { - return names.map(isAllowedResource).every(Boolean); +export function isAllowedResources(...objs: KubeObject[]) { + return objs.map(isAllowedResource).every(Boolean); } /** * Get permissive list permissions over several resources - * @param names Several names of resources + * @param objs Several names of resources * @returns `true` iff `!∀ name ∈ names : !isAllowedResource(name)` */ -export function isAnyAllowedResources(...names: ResourceName[]) { - if (names.length === 0) { +export function isAnyAllowedResources(...objs: KubeObject[]) { + if (objs.length === 0) { return true; } - return names.map(isAllowedResource).some(Boolean); + return objs.map(isAllowedResource).some(Boolean); } diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index eafcf0d8ee..2f90a5280a 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -23,7 +23,8 @@ import type { KubeObjectStore } from "../kube-object.store"; import { action, observable, makeObservable } from "mobx"; import { autoBind } from "../utils"; -import { KubeApi, parseKubeApi } from "./kube-api"; +import type { KubeApi } from "./kube-api"; +import { parseKubeApi } from "./kube-api-parse"; export class ApiManager { private apis = observable.map(); diff --git a/src/renderer/api/endpoints/nodes.api.ts b/src/renderer/api/endpoints/nodes.api.ts index 40dc2aeadb..b6b1b38a25 100644 --- a/src/renderer/api/endpoints/nodes.api.ts +++ b/src/renderer/api/endpoints/nodes.api.ts @@ -136,15 +136,12 @@ export class Node extends KubeObject { } getNodeConditionText() { - const { conditions } = this.status; + const { conditions = [] } = this.status; - if (!conditions) return ""; - - return conditions.reduce((types, current) => { - if (current.status !== "True") return ""; - - return types += ` ${current.type}`; - }, ""); + return conditions + .filter(cond => cond.status === "True") + .map(cond => cond.type) + .join(" "); } getTaints() { diff --git a/src/renderer/api/kube-api-parse.ts b/src/renderer/api/kube-api-parse.ts index b02067fb63..c89cf2cc8f 100644 --- a/src/renderer/api/kube-api-parse.ts +++ b/src/renderer/api/kube-api-parse.ts @@ -24,6 +24,7 @@ import type { KubeObject } from "./kube-object"; import { splitArray } from "../../common/utils"; import { apiManager } from "./api-manager"; +import type { ApiResource } from "../../main/utils/api-resources"; export interface IKubeObjectRef { kind: string; @@ -46,6 +47,15 @@ export interface IKubeApiParsed extends IKubeApiLinkRef { apiVersionWithGroup: string; } +export function createKubeApiBase(resource: ApiResource): string { + if (!resource.group) { + // is legacy API + return `/api/${resource.version}/${resource.name}`; + } + + return `/apis/${resource.group}/${resource.version}/${resource.name}`; +} + export function parseKubeApi(path: string): IKubeApiParsed { path = new URL(path, location.origin).pathname; const [, prefix, ...parts] = path.split("/"); diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 21e370a207..c7e5105004 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -506,5 +506,3 @@ export class KubeApi { } } } - -export * from "./kube-api-parse"; diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 90a8a1a74b..ccbf1fdf31 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -33,7 +33,7 @@ import { boundMethod, cssNames, prevDefault } from "../../utils"; import type { ItemObject } from "../../item.store"; import { Spinner } from "../spinner"; import { ThemeStore } from "../../theme.store"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { kubeSelectedUrlParam, showDetails } from "../kube-object"; interface Props { diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index 1a6db54e78..43dafdcc1e 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -30,7 +30,7 @@ import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object"; import { cssNames } from "../../utils"; import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; interface Props extends KubeObjectDetailsProps { diff --git a/src/renderer/components/+config/config.tsx b/src/renderer/components/+config/config.tsx index e602deaad5..fcb996f408 100644 --- a/src/renderer/components/+config/config.tsx +++ b/src/renderer/components/+config/config.tsx @@ -30,13 +30,14 @@ import { HorizontalPodAutoscalers } from "../+config-autoscalers"; import { isAllowedResource } from "../../api/allowed-resources"; import { LimitRanges } from "../+config-limit-ranges"; import * as routes from "../../../common/routes"; +import { ConfigMap, HorizontalPodAutoscaler, LimitRange, PodDisruptionBudget, ResourceQuota, Secret } from "../../api/endpoints"; @observer export class Config extends React.Component { static get tabRoutes(): TabLayoutRoute[] { const tabs: TabLayoutRoute[] = []; - if (isAllowedResource("configmaps")) { + if (isAllowedResource(ConfigMap)) { tabs.push({ title: "ConfigMaps", component: ConfigMaps, @@ -45,7 +46,7 @@ export class Config extends React.Component { }); } - if (isAllowedResource("secrets")) { + if (isAllowedResource(Secret)) { tabs.push({ title: "Secrets", component: Secrets, @@ -54,7 +55,7 @@ export class Config extends React.Component { }); } - if (isAllowedResource("resourcequotas")) { + if (isAllowedResource(ResourceQuota)) { tabs.push({ title: "Resource Quotas", component: ResourceQuotas, @@ -63,7 +64,7 @@ export class Config extends React.Component { }); } - if (isAllowedResource("limitranges")) { + if (isAllowedResource(LimitRange)) { tabs.push({ title: "Limit Ranges", component: LimitRanges, @@ -72,7 +73,7 @@ export class Config extends React.Component { }); } - if (isAllowedResource("horizontalpodautoscalers")) { + if (isAllowedResource(HorizontalPodAutoscaler)) { tabs.push({ title: "HPA", component: HorizontalPodAutoscalers, @@ -81,7 +82,7 @@ export class Config extends React.Component { }); } - if (isAllowedResource("poddisruptionbudgets")) { + if (isAllowedResource(PodDisruptionBudget)) { tabs.push({ title: "Pod Disruption Budgets", component: PodDisruptionBudgets, diff --git a/src/renderer/components/+events/event-details.tsx b/src/renderer/components/+events/event-details.tsx index 639c4b9dcf..5a533f5fa6 100644 --- a/src/renderer/components/+events/event-details.tsx +++ b/src/renderer/components/+events/event-details.tsx @@ -30,7 +30,7 @@ import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object"; import type { KubeEvent } from "../../api/endpoints/events.api"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { LocaleDate } from "../locale-date"; interface Props extends KubeObjectDetailsProps { diff --git a/src/renderer/components/+events/events.tsx b/src/renderer/components/+events/events.tsx index f0d95bd41e..cb2581dee0 100644 --- a/src/renderer/components/+events/events.tsx +++ b/src/renderer/components/+events/events.tsx @@ -35,7 +35,7 @@ import { Tooltip } from "../tooltip"; import { Link } from "react-router-dom"; import { cssNames, IClassName, stopPropagation } from "../../utils"; import { Icon } from "../icon"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { eventsURL } from "../../../common/routes"; enum columnId { @@ -139,7 +139,7 @@ export class Events extends React.Component { tooltip={`Limited to ${store.limit}`} /> , - title, + title, ...headerPlaceholders }; }; diff --git a/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx b/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx index 1556394a9c..80a2789745 100644 --- a/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx +++ b/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx @@ -26,7 +26,7 @@ import { observer } from "mobx-react"; import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints"; import { Table, TableCell, TableHead, TableRow } from "../table"; import { boundMethod } from "../../utils"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { Link } from "react-router-dom"; import { getDetailsUrl } from "../kube-object"; diff --git a/src/renderer/components/+network/network.tsx b/src/renderer/components/+network/network.tsx index 6a387b334d..8372eaec0e 100644 --- a/src/renderer/components/+network/network.tsx +++ b/src/renderer/components/+network/network.tsx @@ -30,13 +30,14 @@ import { Ingresses } from "../+network-ingresses"; import { NetworkPolicies } from "../+network-policies"; import { isAllowedResource } from "../../api/allowed-resources"; import * as routes from "../../../common/routes"; +import { Endpoint, Ingress, NetworkPolicy, Service } from "../../api/endpoints"; @observer export class Network extends React.Component { static get tabRoutes(): TabLayoutRoute[] { const tabs: TabLayoutRoute[] = []; - if (isAllowedResource("services")) { + if (isAllowedResource(Service)) { tabs.push({ title: "Services", component: Services, @@ -45,7 +46,7 @@ export class Network extends React.Component { }); } - if (isAllowedResource("endpoints")) { + if (isAllowedResource(Endpoint)) { tabs.push({ title: "Endpoints", component: Endpoints, @@ -54,7 +55,7 @@ export class Network extends React.Component { }); } - if (isAllowedResource("ingresses")) { + if (isAllowedResource(Ingress)) { tabs.push({ title: "Ingresses", component: Ingresses, @@ -63,7 +64,7 @@ export class Network extends React.Component { }); } - if (isAllowedResource("networkpolicies")) { + if (isAllowedResource(NetworkPolicy)) { tabs.push({ title: "Network Policies", component: NetworkPolicies, diff --git a/src/renderer/components/+storage/storage.tsx b/src/renderer/components/+storage/storage.tsx index ed5db09a18..b02c30b869 100644 --- a/src/renderer/components/+storage/storage.tsx +++ b/src/renderer/components/+storage/storage.tsx @@ -29,13 +29,14 @@ import { StorageClasses } from "../+storage-classes"; import { PersistentVolumeClaims } from "../+storage-volume-claims"; import { isAllowedResource } from "../../api/allowed-resources"; import * as routes from "../../../common/routes"; +import { PersistentVolume, PersistentVolumeClaim, StorageClass } from "../../api/endpoints"; @observer export class Storage extends React.Component { static get tabRoutes() { const tabs: TabLayoutRoute[] = []; - if (isAllowedResource("persistentvolumeclaims")) { + if (isAllowedResource(PersistentVolumeClaim)) { tabs.push({ title: "Persistent Volume Claims", component: PersistentVolumeClaims, @@ -44,7 +45,7 @@ export class Storage extends React.Component { }); } - if (isAllowedResource("persistentvolumes")) { + if (isAllowedResource(PersistentVolume)) { tabs.push({ title: "Persistent Volumes", component: PersistentVolumes, @@ -53,7 +54,7 @@ export class Storage extends React.Component { }); } - if (isAllowedResource("storageclasses")) { + if (isAllowedResource(StorageClass)) { tabs.push({ title: "Storage Classes", component: StorageClasses, diff --git a/src/renderer/components/+user-management/user-management.tsx b/src/renderer/components/+user-management/user-management.tsx index fdb377907c..d29538ee3c 100644 --- a/src/renderer/components/+user-management/user-management.tsx +++ b/src/renderer/components/+user-management/user-management.tsx @@ -33,13 +33,14 @@ import { ServiceAccounts } from "./+service-accounts"; import { Roles } from "./+roles"; import { RoleBindings } from "./+role-bindings"; import { ClusterRoles } from "./+cluster-roles"; +import { ClusterRole, ClusterRoleBinding, PodSecurityPolicy, Role, RoleBinding, ServiceAccount } from "../../api/endpoints"; @observer export class UserManagement extends React.Component { @computed static get tabRoutes() { const tabRoutes: TabLayoutRoute[] = []; - if (isAllowedResource("serviceaccounts")) { + if (isAllowedResource(ServiceAccount)) { tabRoutes.push({ title: "Service Accounts", component: ServiceAccounts, @@ -48,7 +49,7 @@ export class UserManagement extends React.Component { }); } - if (isAllowedResource("clusterroles")) { + if (isAllowedResource(ClusterRole)) { tabRoutes.push({ title: "Cluster Roles", component: ClusterRoles, @@ -57,7 +58,7 @@ export class UserManagement extends React.Component { }); } - if (isAllowedResource("roles")) { + if (isAllowedResource(Role)) { tabRoutes.push({ title: "Roles", component: Roles, @@ -66,7 +67,7 @@ export class UserManagement extends React.Component { }); } - if (isAllowedResource("clusterrolebindings")) { + if (isAllowedResource(ClusterRoleBinding)) { tabRoutes.push({ title: "Cluster Role Bindings", component: ClusterRoleBindings, @@ -75,7 +76,7 @@ export class UserManagement extends React.Component { }); } - if (isAllowedResource("rolebindings")) { + if (isAllowedResource(RoleBinding)) { tabRoutes.push({ title: "Role Bindings", component: RoleBindings, @@ -84,7 +85,7 @@ export class UserManagement extends React.Component { }); } - if (isAllowedResource("podsecuritypolicies")) { + if (isAllowedResource(PodSecurityPolicy)) { tabRoutes.push({ title: "Pod Security Policies", component: PodSecurityPolicies, diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx index 02cd5b41d4..823353543d 100644 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ b/src/renderer/components/+workloads-jobs/job-details.tsx @@ -35,7 +35,7 @@ import { jobStore } from "./job.store"; import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import type { Job } from "../../api/endpoints"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; interface Props extends KubeObjectDetailsProps { diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx index 503c50e19a..e2a7e5f6f2 100644 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx @@ -25,30 +25,37 @@ 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 { namespaceStore } from "../+namespaces/namespace.store"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; import { isAllowedResource } from "../../api/allowed-resources"; import { boundMethod } from "../../utils"; import { workloadURL } from "../../../common/routes"; import { KubeResource, ResourceNames } from "../../../common/rbac"; +import type { KubeObjectStore } from "../../kube-object.store"; +import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; +import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store"; +import { deploymentStore } from "../+workloads-deployments/deployments.store"; +import { jobStore } from "../+workloads-jobs/job.store"; +import { podsStore } from "../+workloads-pods/pods.store"; +import { replicaSetStore } from "../+workloads-replicasets/replicasets.store"; +import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store"; -const resources: KubeResource[] = [ - "pods", - "deployments", - "statefulsets", - "daemonsets", - "replicasets", - "jobs", - "cronjobs", +const stores: KubeObjectStore[] = [ + podsStore, + deploymentStore, + daemonSetStore, + statefulSetStore, + replicaSetStore, + jobStore, + cronJobStore, ]; @observer export class OverviewStatuses extends React.Component { @boundMethod - renderWorkload(resource: KubeResource): React.ReactElement { - const store = workloadStores[resource]; + renderWorkload(store: KubeObjectStore): React.ReactElement { const items = store.getAllByNs(namespaceStore.contextNamespaces); + const resource = store.api.apiResource as KubeResource; return (
@@ -61,8 +68,8 @@ export class OverviewStatuses extends React.Component { } render() { - const workloads = resources - .filter(isAllowedResource) + const workloads = stores + .filter(store => isAllowedResource(store.api)) .map(this.renderWorkload); return ( diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx index 807d531b20..45b2b02430 100644 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ b/src/renderer/components/+workloads-pods/pods.tsx @@ -35,7 +35,7 @@ import { cssNames, stopPropagation } from "../../utils"; import toPairs from "lodash/toPairs"; import startCase from "lodash/startCase"; import kebabCase from "lodash/kebabCase"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { Badge } from "../badge"; import type { PodsRouteParams } from "../../../common/routes"; diff --git a/src/renderer/components/+workloads/index.ts b/src/renderer/components/+workloads/index.ts index ab4e8fa18c..ab8e32fe40 100644 --- a/src/renderer/components/+workloads/index.ts +++ b/src/renderer/components/+workloads/index.ts @@ -20,4 +20,3 @@ */ export * from "./workloads"; -export * from "./workloads.stores"; diff --git a/src/renderer/components/+workloads/workloads.stores.ts b/src/renderer/components/+workloads/workloads.stores.ts deleted file mode 100644 index a06c17200d..0000000000 --- a/src/renderer/components/+workloads/workloads.stores.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -import type { KubeObjectStore } from "../../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"; - -export const workloadStores: Partial> = { - "pods": podsStore, - "deployments": deploymentStore, - "daemonsets": daemonSetStore, - "statefulsets": statefulSetStore, - "replicasets": replicaSetStore, - "jobs": jobStore, - "cronjobs": cronJobStore, -}; diff --git a/src/renderer/components/+workloads/workloads.tsx b/src/renderer/components/+workloads/workloads.tsx index a911a82be2..5413d6f14e 100644 --- a/src/renderer/components/+workloads/workloads.tsx +++ b/src/renderer/components/+workloads/workloads.tsx @@ -35,13 +35,14 @@ import { CronJobs } from "../+workloads-cronjobs"; import { isAllowedResource } from "../../api/allowed-resources"; import { ReplicaSets } from "../+workloads-replicasets"; import * as routes from "../../../common/routes"; +import { CronJob, DaemonSet, Deployment, Job, Pod, ReplicaSet, StatefulSet } from "../../api/endpoints"; @observer export class Workloads extends React.Component { @computed static get tabRoutes(): TabLayoutRoute[] { const tabs: TabLayoutRoute[] = []; - if (isAllowedResource("pods")) { + if (isAllowedResource(Pod)) { tabs.push({ title: "Pods", component: Pods, @@ -50,7 +51,7 @@ export class Workloads extends React.Component { }); } - if (isAllowedResource("deployments")) { + if (isAllowedResource(Deployment)) { tabs.push({ title: "Deployments", component: Deployments, @@ -59,7 +60,7 @@ export class Workloads extends React.Component { }); } - if (isAllowedResource("daemonsets")) { + if (isAllowedResource(DaemonSet)) { tabs.push({ title: "DaemonSets", component: DaemonSets, @@ -68,7 +69,7 @@ export class Workloads extends React.Component { }); } - if (isAllowedResource("statefulsets")) { + if (isAllowedResource(StatefulSet)) { tabs.push({ title: "StatefulSets", component: StatefulSets, @@ -77,7 +78,7 @@ export class Workloads extends React.Component { }); } - if (isAllowedResource("replicasets")) { + if (isAllowedResource(ReplicaSet)) { tabs.push({ title: "ReplicaSets", component: ReplicaSets, @@ -86,7 +87,7 @@ export class Workloads extends React.Component { }); } - if (isAllowedResource("jobs")) { + if (isAllowedResource(Job)) { tabs.push({ title: "Jobs", component: Jobs, @@ -95,7 +96,7 @@ export class Workloads extends React.Component { }); } - if (isAllowedResource("cronjobs")) { + if (isAllowedResource(CronJob)) { tabs.push({ title: "CronJobs", component: CronJobs, diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index d6af5308bd..fd893a9d58 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -70,12 +70,13 @@ import { Config } from "./+config"; import { Storage } from "./+storage"; import { AllowedResources, isAllowedResources } from "../api/allowed-resources"; import { CubeSpinner } from "./spinner"; +import { KubeEvent, Node, Pod } from "../api/endpoints"; @observer export class App extends React.Component { @computed static get startUrl(): string { - return isAllowedResources("events", "nodes", "pods") ? routes.clusterURL() : routes.workloadsURL(); + return isAllowedResources(KubeEvent, Node, Pod) ? routes.clusterURL() : routes.workloadsURL(); } static async init() { @@ -105,7 +106,7 @@ export class App extends React.Component { // Setup hosted cluster context KubeObjectStore.defaultContext.set(clusterContext); kubeWatchApi.context = clusterContext; - + // This needs to be after the setting up of the contexts await AllowedResources.createInstance(clusterId, () => clusterContext.contextNamespaces).init(); } diff --git a/src/renderer/components/kube-object/kube-object-meta.tsx b/src/renderer/components/kube-object/kube-object-meta.tsx index 98eb539720..29b544c7a0 100644 --- a/src/renderer/components/kube-object/kube-object-meta.tsx +++ b/src/renderer/components/kube-object/kube-object-meta.tsx @@ -22,7 +22,7 @@ import React from "react"; import type { IKubeMetaField, KubeObject } from "../../api/kube-object"; import { DrawerItem, DrawerItemLabels } from "../drawer"; -import { lookupApiLink } from "../../api/kube-api"; +import { lookupApiLink } from "../../api/kube-api-parse"; import { Link } from "react-router-dom"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { LocaleDate } from "../locale-date"; diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx index 1b51302960..7d189bda24 100644 --- a/src/renderer/components/layout/sidebar.tsx +++ b/src/renderer/components/layout/sidebar.tsx @@ -40,6 +40,7 @@ import { SidebarItem } from "./sidebar-item"; import { Apps } from "../+apps"; import * as routes from "../../../common/routes"; import { Config } from "../+config"; +import { CustomResourceDefinition, KubeEvent, Namespace, Node } from "../../api/endpoints"; interface Props { className?: string; @@ -171,7 +172,7 @@ export class Sidebar extends React.Component { id="cluster" text="Cluster" isActive={isActiveRoute(routes.clusterRoute)} - isHidden={!isAllowedResource("nodes")} + isHidden={!isAllowedResource(Node)} url={routes.clusterURL()} icon={} /> @@ -179,7 +180,7 @@ export class Sidebar extends React.Component { id="nodes" text="Nodes" isActive={isActiveRoute(routes.nodesRoute)} - isHidden={!isAllowedResource("nodes")} + isHidden={!isAllowedResource(Node)} url={routes.nodesURL()} icon={} /> @@ -227,7 +228,7 @@ export class Sidebar extends React.Component { id="namespaces" text="Namespaces" isActive={isActiveRoute(routes.namespacesRoute)} - isHidden={!isAllowedResource("namespaces")} + isHidden={!isAllowedResource(Namespace)} url={routes.namespacesURL()} icon={} /> @@ -235,7 +236,7 @@ export class Sidebar extends React.Component { id="events" text="Events" isActive={isActiveRoute(routes.eventRoute)} - isHidden={!isAllowedResource("events")} + isHidden={!isAllowedResource(KubeEvent)} url={routes.eventsURL()} icon={} /> @@ -263,7 +264,7 @@ export class Sidebar extends React.Component { text="Custom Resources" url={routes.crdURL()} isActive={isActiveRoute(routes.crdRoute)} - isHidden={!isAllowedResource("customresourcedefinitions")} + isHidden={!isAllowedResource(CustomResourceDefinition)} icon={} > {this.renderTreeFromTabRoutes(CustomResources.tabRoutes)} diff --git a/src/renderer/initializers/workloads-overview-detail-registry.tsx b/src/renderer/initializers/workloads-overview-detail-registry.tsx index 6dcd5a1076..7e442944cc 100644 --- a/src/renderer/initializers/workloads-overview-detail-registry.tsx +++ b/src/renderer/initializers/workloads-overview-detail-registry.tsx @@ -22,6 +22,7 @@ import React from "react"; import { WorkloadsOverviewDetailRegistry } from "../../extensions/registries"; import { isAllowedResource } from "../api/allowed-resources"; +import { KubeEvent } from "../api/endpoints"; import { Events } from "../components/+events"; import { OverviewStatuses } from "../components/+workloads-overview/overview-statuses"; @@ -37,7 +38,7 @@ export function initWorkloadsOverviewDetailRegistry() { priority: 5, components: { Details: () => ( - isAllowedResource("events") && + isAllowedResource(KubeEvent) && ) } } diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index f2dc8cd0d4..cece2e0575 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -27,7 +27,8 @@ import { KubeObject, KubeStatus } from "./api/kube-object"; import type { IKubeWatchEvent } from "./api/kube-watch-api"; import { ItemStore } from "./item.store"; import { apiManager } from "./api/api-manager"; -import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi, parseKubeApi } from "./api/kube-api"; +import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi } from "./api/kube-api"; +import { parseKubeApi } from "./api/kube-api-parse"; import type { KubeJsonApiData } from "./api/kube-json-api"; import { Notifications } from "./components/notifications"; import { isAllowedResource } from "./api/allowed-resources"; @@ -139,7 +140,7 @@ export abstract class KubeObjectStore extends ItemSt } protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise { - if (isAllowedResource(api.apiResource)) { + if (isAllowedResource(api)) { if (!api.isNamespaced) { return api.list({ reqInit }, this.query); } @@ -281,7 +282,7 @@ export abstract class KubeObjectStore extends ItemSt async update(item: T, data: Partial): Promise { const newItem = await item.update(data); - + ensureObjectSelfLink(this.api, newItem); const index = this.items.findIndex(item => item.getId() === newItem.getId());