1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Switch to keeping track of ApiBase instead of resourceName

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-06-18 10:05:43 -04:00
parent b14e77a616
commit 720468b120
27 changed files with 123 additions and 135 deletions

View File

@ -19,7 +19,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
export type KubeResource = export type KubeResource =
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" |
"secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | "secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |

View File

@ -35,6 +35,7 @@ import { ExtendedObservableMap, toJS } from "../common/utils";
import { getClusterResources } from "./utils/api-resources"; import { getClusterResources } from "./utils/api-resources";
import { asyncThrottle } from "../common/utils/async-throttle"; import { asyncThrottle } from "../common/utils/async-throttle";
import pLimit from "p-limit"; import pLimit from "p-limit";
import { createKubeApiBase } from "../renderer/api/kube-api-parse";
export enum ClusterStatus { export enum ClusterStatus {
AccessGranted = 2, AccessGranted = 2,
@ -527,17 +528,20 @@ export class Cluster implements ClusterModel, ClusterState {
for (const [group, versions] of groups) { for (const [group, versions] of groups) {
for (const [version, resources] of versions) { for (const [version, resources] of versions) {
for (const resource of resources.keys()) { for (const [resource, apiInfo] of resources) {
const canList = await this.canI({ const attr: V1ResourceAttributes = {
resource, resource,
version, version,
group, group,
namespace,
verb: "list", verb: "list",
}); };
if (canList) { if (apiInfo.namespaced) {
isAllowed.add(resource); attr.namespace = namespace;
}
if (await this.canI(attr)) {
isAllowed.add(createKubeApiBase(apiInfo));
} }
} }
} }

View File

@ -22,16 +22,19 @@
import { action, makeObservable, observable, reaction } from "mobx"; import { action, makeObservable, observable, reaction } from "mobx";
import type { ClusterId } from "../../common/cluster-store"; import type { ClusterId } from "../../common/cluster-store";
import { ClusterResourceIsAllowedChannel, ClusterGetResourcesChannel, requestMain } from "../../common/ipc"; 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 type { ApiResourceMap } from "../../main/utils/api-resources";
import { ObservableTimer } from "../../common/utils/observable-timer"; import { ObservableTimer } from "../../common/utils/observable-timer";
import { Notifications } from "../components/notifications"; import { Notifications } from "../components/notifications";
type NamespaceName = string; type NamespaceName = string;
type ResourceName = string;
export interface KubeObject {
apiBase: string;
}
export class AllowedResources extends Singleton { export class AllowedResources extends Singleton {
protected allowedResources = observable.set<ResourceName>(); protected allowedResources = observable.set<string>();
@observable public resources: ApiResourceMap; @observable public resources: ApiResourceMap;
/** /**
@ -51,6 +54,7 @@ export class AllowedResources extends Singleton {
async init() { async init() {
try { try {
this.resources = await requestMain(ClusterGetResourcesChannel, this.clusterId); this.resources = await requestMain(ClusterGetResourcesChannel, this.clusterId);
console.log(toJS(this.resources));
} catch (error) { } catch (error) {
console.error("[ALLOWED-RESOURCES]: failed to initialize resources", error); console.error("[ALLOWED-RESOURCES]: failed to initialize resources", error);
Notifications.error("Failed to initialize resources"); Notifications.error("Failed to initialize resources");
@ -77,42 +81,42 @@ export class AllowedResources extends Singleton {
/** /**
* Get the permissive list permissions of `name` over `namespaces` * 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) * @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 * @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 { isAllowed(obj: KubeObject): boolean {
return this.allowedResources.has(name); return this.allowedResources.has(obj.apiBase);
} }
} }
/** /**
* Get list permissions for a single resource * 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 * @returns `true` if the resource exists on the cluster and the cluster has list permissions for that resource
*/ */
export function isAllowedResource(name: ResourceName) { export function isAllowedResource(obj: KubeObject) {
return AllowedResources.getInstance().isAllowed(name); return AllowedResources.getInstance().isAllowed(obj);
} }
/** /**
* Get list permissions for several resources * 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)` * @returns `true` iff `∀ name ∈ names : isAllowedResource(name)`
*/ */
export function isAllowedResources(...names: ResourceName[]) { export function isAllowedResources(...objs: KubeObject[]) {
return names.map(isAllowedResource).every(Boolean); return objs.map(isAllowedResource).every(Boolean);
} }
/** /**
* Get permissive list permissions over several resources * 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)` * @returns `true` iff `!∀ name ∈ names : !isAllowedResource(name)`
*/ */
export function isAnyAllowedResources(...names: ResourceName[]) { export function isAnyAllowedResources(...objs: KubeObject[]) {
if (names.length === 0) { if (objs.length === 0) {
return true; return true;
} }
return names.map(isAllowedResource).some(Boolean); return objs.map(isAllowedResource).some(Boolean);
} }

View File

@ -23,7 +23,8 @@ import type { KubeObjectStore } from "../kube-object.store";
import { action, observable, makeObservable } from "mobx"; import { action, observable, makeObservable } from "mobx";
import { autoBind } from "../utils"; 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 { export class ApiManager {
private apis = observable.map<string, KubeApi>(); private apis = observable.map<string, KubeApi>();

View File

@ -136,15 +136,12 @@ export class Node extends KubeObject {
} }
getNodeConditionText() { getNodeConditionText() {
const { conditions } = this.status; const { conditions = [] } = this.status;
if (!conditions) return ""; return conditions
.filter(cond => cond.status === "True")
return conditions.reduce((types, current) => { .map(cond => cond.type)
if (current.status !== "True") return ""; .join(" ");
return types += ` ${current.type}`;
}, "");
} }
getTaints() { getTaints() {

View File

@ -24,6 +24,7 @@
import type { KubeObject } from "./kube-object"; import type { KubeObject } from "./kube-object";
import { splitArray } from "../../common/utils"; import { splitArray } from "../../common/utils";
import { apiManager } from "./api-manager"; import { apiManager } from "./api-manager";
import type { ApiResource } from "../../main/utils/api-resources";
export interface IKubeObjectRef { export interface IKubeObjectRef {
kind: string; kind: string;
@ -46,6 +47,15 @@ export interface IKubeApiParsed extends IKubeApiLinkRef {
apiVersionWithGroup: string; 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 { export function parseKubeApi(path: string): IKubeApiParsed {
path = new URL(path, location.origin).pathname; path = new URL(path, location.origin).pathname;
const [, prefix, ...parts] = path.split("/"); const [, prefix, ...parts] = path.split("/");

View File

@ -506,5 +506,3 @@ export class KubeApi<T extends KubeObject = any> {
} }
} }
} }
export * from "./kube-api-parse";

View File

@ -33,7 +33,7 @@ import { boundMethod, cssNames, prevDefault } from "../../utils";
import type { ItemObject } from "../../item.store"; import type { ItemObject } from "../../item.store";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import { ThemeStore } from "../../theme.store"; import { ThemeStore } from "../../theme.store";
import { lookupApiLink } from "../../api/kube-api"; import { lookupApiLink } from "../../api/kube-api-parse";
import { kubeSelectedUrlParam, showDetails } from "../kube-object"; import { kubeSelectedUrlParam, showDetails } from "../kube-object";
interface Props { interface Props {

View File

@ -30,7 +30,7 @@ import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api"; import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api";
import { Table, TableCell, TableHead, TableRow } from "../table"; 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"; import { KubeObjectMeta } from "../kube-object/kube-object-meta";
interface Props extends KubeObjectDetailsProps<HorizontalPodAutoscaler> { interface Props extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {

View File

@ -30,13 +30,14 @@ import { HorizontalPodAutoscalers } from "../+config-autoscalers";
import { isAllowedResource } from "../../api/allowed-resources"; import { isAllowedResource } from "../../api/allowed-resources";
import { LimitRanges } from "../+config-limit-ranges"; import { LimitRanges } from "../+config-limit-ranges";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
import { ConfigMap, HorizontalPodAutoscaler, LimitRange, PodDisruptionBudget, ResourceQuota, Secret } from "../../api/endpoints";
@observer @observer
export class Config extends React.Component { export class Config extends React.Component {
static get tabRoutes(): TabLayoutRoute[] { static get tabRoutes(): TabLayoutRoute[] {
const tabs: TabLayoutRoute[] = []; const tabs: TabLayoutRoute[] = [];
if (isAllowedResource("configmaps")) { if (isAllowedResource(ConfigMap)) {
tabs.push({ tabs.push({
title: "ConfigMaps", title: "ConfigMaps",
component: ConfigMaps, component: ConfigMaps,
@ -45,7 +46,7 @@ export class Config extends React.Component {
}); });
} }
if (isAllowedResource("secrets")) { if (isAllowedResource(Secret)) {
tabs.push({ tabs.push({
title: "Secrets", title: "Secrets",
component: Secrets, component: Secrets,
@ -54,7 +55,7 @@ export class Config extends React.Component {
}); });
} }
if (isAllowedResource("resourcequotas")) { if (isAllowedResource(ResourceQuota)) {
tabs.push({ tabs.push({
title: "Resource Quotas", title: "Resource Quotas",
component: ResourceQuotas, component: ResourceQuotas,
@ -63,7 +64,7 @@ export class Config extends React.Component {
}); });
} }
if (isAllowedResource("limitranges")) { if (isAllowedResource(LimitRange)) {
tabs.push({ tabs.push({
title: "Limit Ranges", title: "Limit Ranges",
component: LimitRanges, component: LimitRanges,
@ -72,7 +73,7 @@ export class Config extends React.Component {
}); });
} }
if (isAllowedResource("horizontalpodautoscalers")) { if (isAllowedResource(HorizontalPodAutoscaler)) {
tabs.push({ tabs.push({
title: "HPA", title: "HPA",
component: HorizontalPodAutoscalers, component: HorizontalPodAutoscalers,
@ -81,7 +82,7 @@ export class Config extends React.Component {
}); });
} }
if (isAllowedResource("poddisruptionbudgets")) { if (isAllowedResource(PodDisruptionBudget)) {
tabs.push({ tabs.push({
title: "Pod Disruption Budgets", title: "Pod Disruption Budgets",
component: PodDisruptionBudgets, component: PodDisruptionBudgets,

View File

@ -30,7 +30,7 @@ import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object";
import type { KubeEvent } from "../../api/endpoints/events.api"; import type { KubeEvent } from "../../api/endpoints/events.api";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../table"; 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"; import { LocaleDate } from "../locale-date";
interface Props extends KubeObjectDetailsProps<KubeEvent> { interface Props extends KubeObjectDetailsProps<KubeEvent> {

View File

@ -35,7 +35,7 @@ import { Tooltip } from "../tooltip";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { cssNames, IClassName, stopPropagation } from "../../utils"; import { cssNames, IClassName, stopPropagation } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { lookupApiLink } from "../../api/kube-api"; import { lookupApiLink } from "../../api/kube-api-parse";
import { eventsURL } from "../../../common/routes"; import { eventsURL } from "../../../common/routes";
enum columnId { enum columnId {

View File

@ -26,7 +26,7 @@ import { observer } from "mobx-react";
import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints"; import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { boundMethod } from "../../utils"; import { boundMethod } from "../../utils";
import { lookupApiLink } from "../../api/kube-api"; import { lookupApiLink } from "../../api/kube-api-parse";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { getDetailsUrl } from "../kube-object"; import { getDetailsUrl } from "../kube-object";

View File

@ -30,13 +30,14 @@ import { Ingresses } from "../+network-ingresses";
import { NetworkPolicies } from "../+network-policies"; import { NetworkPolicies } from "../+network-policies";
import { isAllowedResource } from "../../api/allowed-resources"; import { isAllowedResource } from "../../api/allowed-resources";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
import { Endpoint, Ingress, NetworkPolicy, Service } from "../../api/endpoints";
@observer @observer
export class Network extends React.Component { export class Network extends React.Component {
static get tabRoutes(): TabLayoutRoute[] { static get tabRoutes(): TabLayoutRoute[] {
const tabs: TabLayoutRoute[] = []; const tabs: TabLayoutRoute[] = [];
if (isAllowedResource("services")) { if (isAllowedResource(Service)) {
tabs.push({ tabs.push({
title: "Services", title: "Services",
component: Services, component: Services,
@ -45,7 +46,7 @@ export class Network extends React.Component {
}); });
} }
if (isAllowedResource("endpoints")) { if (isAllowedResource(Endpoint)) {
tabs.push({ tabs.push({
title: "Endpoints", title: "Endpoints",
component: Endpoints, component: Endpoints,
@ -54,7 +55,7 @@ export class Network extends React.Component {
}); });
} }
if (isAllowedResource("ingresses")) { if (isAllowedResource(Ingress)) {
tabs.push({ tabs.push({
title: "Ingresses", title: "Ingresses",
component: Ingresses, component: Ingresses,
@ -63,7 +64,7 @@ export class Network extends React.Component {
}); });
} }
if (isAllowedResource("networkpolicies")) { if (isAllowedResource(NetworkPolicy)) {
tabs.push({ tabs.push({
title: "Network Policies", title: "Network Policies",
component: NetworkPolicies, component: NetworkPolicies,

View File

@ -29,13 +29,14 @@ import { StorageClasses } from "../+storage-classes";
import { PersistentVolumeClaims } from "../+storage-volume-claims"; import { PersistentVolumeClaims } from "../+storage-volume-claims";
import { isAllowedResource } from "../../api/allowed-resources"; import { isAllowedResource } from "../../api/allowed-resources";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
import { PersistentVolume, PersistentVolumeClaim, StorageClass } from "../../api/endpoints";
@observer @observer
export class Storage extends React.Component { export class Storage extends React.Component {
static get tabRoutes() { static get tabRoutes() {
const tabs: TabLayoutRoute[] = []; const tabs: TabLayoutRoute[] = [];
if (isAllowedResource("persistentvolumeclaims")) { if (isAllowedResource(PersistentVolumeClaim)) {
tabs.push({ tabs.push({
title: "Persistent Volume Claims", title: "Persistent Volume Claims",
component: PersistentVolumeClaims, component: PersistentVolumeClaims,
@ -44,7 +45,7 @@ export class Storage extends React.Component {
}); });
} }
if (isAllowedResource("persistentvolumes")) { if (isAllowedResource(PersistentVolume)) {
tabs.push({ tabs.push({
title: "Persistent Volumes", title: "Persistent Volumes",
component: PersistentVolumes, component: PersistentVolumes,
@ -53,7 +54,7 @@ export class Storage extends React.Component {
}); });
} }
if (isAllowedResource("storageclasses")) { if (isAllowedResource(StorageClass)) {
tabs.push({ tabs.push({
title: "Storage Classes", title: "Storage Classes",
component: StorageClasses, component: StorageClasses,

View File

@ -33,13 +33,14 @@ import { ServiceAccounts } from "./+service-accounts";
import { Roles } from "./+roles"; import { Roles } from "./+roles";
import { RoleBindings } from "./+role-bindings"; import { RoleBindings } from "./+role-bindings";
import { ClusterRoles } from "./+cluster-roles"; import { ClusterRoles } from "./+cluster-roles";
import { ClusterRole, ClusterRoleBinding, PodSecurityPolicy, Role, RoleBinding, ServiceAccount } from "../../api/endpoints";
@observer @observer
export class UserManagement extends React.Component { export class UserManagement extends React.Component {
@computed static get tabRoutes() { @computed static get tabRoutes() {
const tabRoutes: TabLayoutRoute[] = []; const tabRoutes: TabLayoutRoute[] = [];
if (isAllowedResource("serviceaccounts")) { if (isAllowedResource(ServiceAccount)) {
tabRoutes.push({ tabRoutes.push({
title: "Service Accounts", title: "Service Accounts",
component: ServiceAccounts, component: ServiceAccounts,
@ -48,7 +49,7 @@ export class UserManagement extends React.Component {
}); });
} }
if (isAllowedResource("clusterroles")) { if (isAllowedResource(ClusterRole)) {
tabRoutes.push({ tabRoutes.push({
title: "Cluster Roles", title: "Cluster Roles",
component: ClusterRoles, component: ClusterRoles,
@ -57,7 +58,7 @@ export class UserManagement extends React.Component {
}); });
} }
if (isAllowedResource("roles")) { if (isAllowedResource(Role)) {
tabRoutes.push({ tabRoutes.push({
title: "Roles", title: "Roles",
component: Roles, component: Roles,
@ -66,7 +67,7 @@ export class UserManagement extends React.Component {
}); });
} }
if (isAllowedResource("clusterrolebindings")) { if (isAllowedResource(ClusterRoleBinding)) {
tabRoutes.push({ tabRoutes.push({
title: "Cluster Role Bindings", title: "Cluster Role Bindings",
component: ClusterRoleBindings, component: ClusterRoleBindings,
@ -75,7 +76,7 @@ export class UserManagement extends React.Component {
}); });
} }
if (isAllowedResource("rolebindings")) { if (isAllowedResource(RoleBinding)) {
tabRoutes.push({ tabRoutes.push({
title: "Role Bindings", title: "Role Bindings",
component: RoleBindings, component: RoleBindings,
@ -84,7 +85,7 @@ export class UserManagement extends React.Component {
}); });
} }
if (isAllowedResource("podsecuritypolicies")) { if (isAllowedResource(PodSecurityPolicy)) {
tabRoutes.push({ tabRoutes.push({
title: "Pod Security Policies", title: "Pod Security Policies",
component: PodSecurityPolicies, component: PodSecurityPolicies,

View File

@ -35,7 +35,7 @@ import { jobStore } from "./job.store";
import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object";
import type { Job } from "../../api/endpoints"; import type { Job } from "../../api/endpoints";
import { PodDetailsList } from "../+workloads-pods/pod-details-list"; 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"; import { KubeObjectMeta } from "../kube-object/kube-object-meta";
interface Props extends KubeObjectDetailsProps<Job> { interface Props extends KubeObjectDetailsProps<Job> {

View File

@ -25,30 +25,37 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { OverviewWorkloadStatus } from "./overview-workload-status"; import { OverviewWorkloadStatus } from "./overview-workload-status";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { workloadStores } from "../+workloads";
import { namespaceStore } from "../+namespaces/namespace.store"; import { namespaceStore } from "../+namespaces/namespace.store";
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
import { isAllowedResource } from "../../api/allowed-resources"; import { isAllowedResource } from "../../api/allowed-resources";
import { boundMethod } from "../../utils"; import { boundMethod } from "../../utils";
import { workloadURL } from "../../../common/routes"; import { workloadURL } from "../../../common/routes";
import { KubeResource, ResourceNames } from "../../../common/rbac"; 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[] = [ const stores: KubeObjectStore[] = [
"pods", podsStore,
"deployments", deploymentStore,
"statefulsets", daemonSetStore,
"daemonsets", statefulSetStore,
"replicasets", replicaSetStore,
"jobs", jobStore,
"cronjobs", cronJobStore,
]; ];
@observer @observer
export class OverviewStatuses extends React.Component { export class OverviewStatuses extends React.Component {
@boundMethod @boundMethod
renderWorkload(resource: KubeResource): React.ReactElement { renderWorkload(store: KubeObjectStore): React.ReactElement {
const store = workloadStores[resource];
const items = store.getAllByNs(namespaceStore.contextNamespaces); const items = store.getAllByNs(namespaceStore.contextNamespaces);
const resource = store.api.apiResource as KubeResource;
return ( return (
<div className="workload" key={resource}> <div className="workload" key={resource}>
@ -61,8 +68,8 @@ export class OverviewStatuses extends React.Component {
} }
render() { render() {
const workloads = resources const workloads = stores
.filter(isAllowedResource) .filter(store => isAllowedResource(store.api))
.map(this.renderWorkload); .map(this.renderWorkload);
return ( return (

View File

@ -35,7 +35,7 @@ import { cssNames, stopPropagation } from "../../utils";
import toPairs from "lodash/toPairs"; import toPairs from "lodash/toPairs";
import startCase from "lodash/startCase"; import startCase from "lodash/startCase";
import kebabCase from "lodash/kebabCase"; 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 { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { Badge } from "../badge"; import { Badge } from "../badge";
import type { PodsRouteParams } from "../../../common/routes"; import type { PodsRouteParams } from "../../../common/routes";

View File

@ -20,4 +20,3 @@
*/ */
export * from "./workloads"; export * from "./workloads";
export * from "./workloads.stores";

View File

@ -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<Record<KubeResource, KubeObjectStore>> = {
"pods": podsStore,
"deployments": deploymentStore,
"daemonsets": daemonSetStore,
"statefulsets": statefulSetStore,
"replicasets": replicaSetStore,
"jobs": jobStore,
"cronjobs": cronJobStore,
};

View File

@ -35,13 +35,14 @@ import { CronJobs } from "../+workloads-cronjobs";
import { isAllowedResource } from "../../api/allowed-resources"; import { isAllowedResource } from "../../api/allowed-resources";
import { ReplicaSets } from "../+workloads-replicasets"; import { ReplicaSets } from "../+workloads-replicasets";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
import { CronJob, DaemonSet, Deployment, Job, Pod, ReplicaSet, StatefulSet } from "../../api/endpoints";
@observer @observer
export class Workloads extends React.Component { export class Workloads extends React.Component {
@computed static get tabRoutes(): TabLayoutRoute[] { @computed static get tabRoutes(): TabLayoutRoute[] {
const tabs: TabLayoutRoute[] = []; const tabs: TabLayoutRoute[] = [];
if (isAllowedResource("pods")) { if (isAllowedResource(Pod)) {
tabs.push({ tabs.push({
title: "Pods", title: "Pods",
component: Pods, component: Pods,
@ -50,7 +51,7 @@ export class Workloads extends React.Component {
}); });
} }
if (isAllowedResource("deployments")) { if (isAllowedResource(Deployment)) {
tabs.push({ tabs.push({
title: "Deployments", title: "Deployments",
component: Deployments, component: Deployments,
@ -59,7 +60,7 @@ export class Workloads extends React.Component {
}); });
} }
if (isAllowedResource("daemonsets")) { if (isAllowedResource(DaemonSet)) {
tabs.push({ tabs.push({
title: "DaemonSets", title: "DaemonSets",
component: DaemonSets, component: DaemonSets,
@ -68,7 +69,7 @@ export class Workloads extends React.Component {
}); });
} }
if (isAllowedResource("statefulsets")) { if (isAllowedResource(StatefulSet)) {
tabs.push({ tabs.push({
title: "StatefulSets", title: "StatefulSets",
component: StatefulSets, component: StatefulSets,
@ -77,7 +78,7 @@ export class Workloads extends React.Component {
}); });
} }
if (isAllowedResource("replicasets")) { if (isAllowedResource(ReplicaSet)) {
tabs.push({ tabs.push({
title: "ReplicaSets", title: "ReplicaSets",
component: ReplicaSets, component: ReplicaSets,
@ -86,7 +87,7 @@ export class Workloads extends React.Component {
}); });
} }
if (isAllowedResource("jobs")) { if (isAllowedResource(Job)) {
tabs.push({ tabs.push({
title: "Jobs", title: "Jobs",
component: Jobs, component: Jobs,
@ -95,7 +96,7 @@ export class Workloads extends React.Component {
}); });
} }
if (isAllowedResource("cronjobs")) { if (isAllowedResource(CronJob)) {
tabs.push({ tabs.push({
title: "CronJobs", title: "CronJobs",
component: CronJobs, component: CronJobs,

View File

@ -70,12 +70,13 @@ import { Config } from "./+config";
import { Storage } from "./+storage"; import { Storage } from "./+storage";
import { AllowedResources, isAllowedResources } from "../api/allowed-resources"; import { AllowedResources, isAllowedResources } from "../api/allowed-resources";
import { CubeSpinner } from "./spinner"; import { CubeSpinner } from "./spinner";
import { KubeEvent, Node, Pod } from "../api/endpoints";
@observer @observer
export class App extends React.Component { export class App extends React.Component {
@computed @computed
static get startUrl(): string { 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() { static async init() {

View File

@ -22,7 +22,7 @@
import React from "react"; import React from "react";
import type { IKubeMetaField, KubeObject } from "../../api/kube-object"; import type { IKubeMetaField, KubeObject } from "../../api/kube-object";
import { DrawerItem, DrawerItemLabels } from "../drawer"; 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 { Link } from "react-router-dom";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { LocaleDate } from "../locale-date"; import { LocaleDate } from "../locale-date";

View File

@ -40,6 +40,7 @@ import { SidebarItem } from "./sidebar-item";
import { Apps } from "../+apps"; import { Apps } from "../+apps";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
import { Config } from "../+config"; import { Config } from "../+config";
import { CustomResourceDefinition, KubeEvent, Namespace, Node } from "../../api/endpoints";
interface Props { interface Props {
className?: string; className?: string;
@ -171,7 +172,7 @@ export class Sidebar extends React.Component<Props> {
id="cluster" id="cluster"
text="Cluster" text="Cluster"
isActive={isActiveRoute(routes.clusterRoute)} isActive={isActiveRoute(routes.clusterRoute)}
isHidden={!isAllowedResource("nodes")} isHidden={!isAllowedResource(Node)}
url={routes.clusterURL()} url={routes.clusterURL()}
icon={<Icon svg="kube"/>} icon={<Icon svg="kube"/>}
/> />
@ -179,7 +180,7 @@ export class Sidebar extends React.Component<Props> {
id="nodes" id="nodes"
text="Nodes" text="Nodes"
isActive={isActiveRoute(routes.nodesRoute)} isActive={isActiveRoute(routes.nodesRoute)}
isHidden={!isAllowedResource("nodes")} isHidden={!isAllowedResource(Node)}
url={routes.nodesURL()} url={routes.nodesURL()}
icon={<Icon svg="nodes"/>} icon={<Icon svg="nodes"/>}
/> />
@ -227,7 +228,7 @@ export class Sidebar extends React.Component<Props> {
id="namespaces" id="namespaces"
text="Namespaces" text="Namespaces"
isActive={isActiveRoute(routes.namespacesRoute)} isActive={isActiveRoute(routes.namespacesRoute)}
isHidden={!isAllowedResource("namespaces")} isHidden={!isAllowedResource(Namespace)}
url={routes.namespacesURL()} url={routes.namespacesURL()}
icon={<Icon material="layers"/>} icon={<Icon material="layers"/>}
/> />
@ -235,7 +236,7 @@ export class Sidebar extends React.Component<Props> {
id="events" id="events"
text="Events" text="Events"
isActive={isActiveRoute(routes.eventRoute)} isActive={isActiveRoute(routes.eventRoute)}
isHidden={!isAllowedResource("events")} isHidden={!isAllowedResource(KubeEvent)}
url={routes.eventsURL()} url={routes.eventsURL()}
icon={<Icon material="access_time"/>} icon={<Icon material="access_time"/>}
/> />
@ -263,7 +264,7 @@ export class Sidebar extends React.Component<Props> {
text="Custom Resources" text="Custom Resources"
url={routes.crdURL()} url={routes.crdURL()}
isActive={isActiveRoute(routes.crdRoute)} isActive={isActiveRoute(routes.crdRoute)}
isHidden={!isAllowedResource("customresourcedefinitions")} isHidden={!isAllowedResource(CustomResourceDefinition)}
icon={<Icon material="extension" />} icon={<Icon material="extension" />}
> >
{this.renderTreeFromTabRoutes(CustomResources.tabRoutes)} {this.renderTreeFromTabRoutes(CustomResources.tabRoutes)}

View File

@ -22,6 +22,7 @@
import React from "react"; import React from "react";
import { WorkloadsOverviewDetailRegistry } from "../../extensions/registries"; import { WorkloadsOverviewDetailRegistry } from "../../extensions/registries";
import { isAllowedResource } from "../api/allowed-resources"; import { isAllowedResource } from "../api/allowed-resources";
import { KubeEvent } from "../api/endpoints";
import { Events } from "../components/+events"; import { Events } from "../components/+events";
import { OverviewStatuses } from "../components/+workloads-overview/overview-statuses"; import { OverviewStatuses } from "../components/+workloads-overview/overview-statuses";
@ -37,7 +38,7 @@ export function initWorkloadsOverviewDetailRegistry() {
priority: 5, priority: 5,
components: { components: {
Details: () => ( Details: () => (
isAllowedResource("events") && <Events compact hideFilters className="box grow" /> isAllowedResource(KubeEvent) && <Events compact hideFilters className="box grow" />
) )
} }
} }

View File

@ -27,7 +27,8 @@ import { KubeObject, KubeStatus } from "./api/kube-object";
import type { IKubeWatchEvent } from "./api/kube-watch-api"; import type { IKubeWatchEvent } from "./api/kube-watch-api";
import { ItemStore } from "./item.store"; import { ItemStore } from "./item.store";
import { apiManager } from "./api/api-manager"; 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 type { KubeJsonApiData } from "./api/kube-json-api";
import { Notifications } from "./components/notifications"; import { Notifications } from "./components/notifications";
import { isAllowedResource } from "./api/allowed-resources"; import { isAllowedResource } from "./api/allowed-resources";
@ -139,7 +140,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise<T[]> { protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise<T[]> {
if (isAllowedResource(api.apiResource)) { if (isAllowedResource(api)) {
if (!api.isNamespaced) { if (!api.isNamespaced) {
return api.list({ reqInit }, this.query); return api.list({ reqInit }, this.query);
} }