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.
*/
export type KubeResource =
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" |
"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 { 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));
}
}
}

View File

@ -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<ResourceName>();
protected allowedResources = observable.set<string>();
@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);
}

View File

@ -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<string, KubeApi>();

View File

@ -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() {

View File

@ -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("/");

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 { 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 {

View File

@ -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<HorizontalPodAutoscaler> {

View File

@ -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,

View File

@ -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<KubeEvent> {

View File

@ -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<Props> {
tooltip={`Limited to ${store.limit}`}
/>
</>,
title,
title,
...headerPlaceholders
};
};

View File

@ -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";

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<Job> {

View File

@ -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 (
<div className="workload" key={resource}>
@ -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 (

View File

@ -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";

View File

@ -20,4 +20,3 @@
*/
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 { 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,

View File

@ -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();
}

View File

@ -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";

View File

@ -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<Props> {
id="cluster"
text="Cluster"
isActive={isActiveRoute(routes.clusterRoute)}
isHidden={!isAllowedResource("nodes")}
isHidden={!isAllowedResource(Node)}
url={routes.clusterURL()}
icon={<Icon svg="kube"/>}
/>
@ -179,7 +180,7 @@ export class Sidebar extends React.Component<Props> {
id="nodes"
text="Nodes"
isActive={isActiveRoute(routes.nodesRoute)}
isHidden={!isAllowedResource("nodes")}
isHidden={!isAllowedResource(Node)}
url={routes.nodesURL()}
icon={<Icon svg="nodes"/>}
/>
@ -227,7 +228,7 @@ export class Sidebar extends React.Component<Props> {
id="namespaces"
text="Namespaces"
isActive={isActiveRoute(routes.namespacesRoute)}
isHidden={!isAllowedResource("namespaces")}
isHidden={!isAllowedResource(Namespace)}
url={routes.namespacesURL()}
icon={<Icon material="layers"/>}
/>
@ -235,7 +236,7 @@ export class Sidebar extends React.Component<Props> {
id="events"
text="Events"
isActive={isActiveRoute(routes.eventRoute)}
isHidden={!isAllowedResource("events")}
isHidden={!isAllowedResource(KubeEvent)}
url={routes.eventsURL()}
icon={<Icon material="access_time"/>}
/>
@ -263,7 +264,7 @@ export class Sidebar extends React.Component<Props> {
text="Custom Resources"
url={routes.crdURL()}
isActive={isActiveRoute(routes.crdRoute)}
isHidden={!isAllowedResource("customresourcedefinitions")}
isHidden={!isAllowedResource(CustomResourceDefinition)}
icon={<Icon material="extension" />}
>
{this.renderTreeFromTabRoutes(CustomResources.tabRoutes)}

View File

@ -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") && <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 { 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<T extends KubeObject = any> extends ItemSt
}
protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise<T[]> {
if (isAllowedResource(api.apiResource)) {
if (isAllowedResource(api)) {
if (!api.isNamespaced) {
return api.list({ reqInit }, this.query);
}
@ -281,7 +282,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
async update(item: T, data: Partial<T>): Promise<T> {
const newItem = await item.update<T>(data);
ensureObjectSelfLink(this.api, newItem);
const index = this.items.findIndex(item => item.getId() === newItem.getId());