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:
parent
b14e77a616
commit
720468b120
@ -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" |
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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("/");
|
||||
|
||||
@ -506,5 +506,3 @@ export class KubeApi<T extends KubeObject = any> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export * from "./kube-api-parse";
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -20,4 +20,3 @@
|
||||
*/
|
||||
|
||||
export * from "./workloads";
|
||||
export * from "./workloads.stores";
|
||||
|
||||
@ -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,
|
||||
};
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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)}
|
||||
|
||||
@ -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" />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user