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

loading k8s resources into stores per selected namespaces -- part 1

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-01-07 15:54:22 +02:00
parent 02bc210f56
commit 47bea52160
6 changed files with 50 additions and 63 deletions

View File

@ -182,7 +182,7 @@ export class Cluster implements ClusterModel, ClusterState {
*/ */
@observable metadata: ClusterMetadata = {}; @observable metadata: ClusterMetadata = {};
/** /**
* List of allowed namespaces * List of allowed namespaces verified via K8S::SelfSubjectAccessReview api
* *
* @observable * @observable
*/ */
@ -195,7 +195,7 @@ export class Cluster implements ClusterModel, ClusterState {
*/ */
@observable allowedResources: string[] = []; @observable allowedResources: string[] = [];
/** /**
* List of accessible namespaces * List of accessible namespaces provided by user in the Cluster Settings
* *
* @observable * @observable
*/ */

View File

@ -11,7 +11,7 @@ import { apiPrefix, isDevelopment } from "../../common/vars";
import { getHostedCluster } from "../../common/cluster-store"; import { getHostedCluster } from "../../common/cluster-store";
export interface IKubeWatchEvent<T = any> { export interface IKubeWatchEvent<T = any> {
type: "ADDED" | "MODIFIED" | "DELETED"; type: "ADDED" | "MODIFIED" | "DELETED" | "ERROR";
object?: T; object?: T;
} }
@ -158,6 +158,11 @@ export class KubeWatchApi {
addListener(store: KubeObjectStore, callback: (evt: IKubeWatchEvent) => void) { addListener(store: KubeObjectStore, callback: (evt: IKubeWatchEvent) => void) {
const listener = (evt: IKubeWatchEvent<KubeJsonApiData>) => { const listener = (evt: IKubeWatchEvent<KubeJsonApiData>) => {
if (evt.type === "ERROR") {
// console.error(evt.object);
return; // fixme: too old resource version (e.g. reproduce: quickly jump btw pages)
}
const { namespace, resourceVersion } = evt.object.metadata; const { namespace, resourceVersion } = evt.object.metadata;
const api = apiManager.getApiByKind(evt.object.kind, evt.object.apiVersion); const api = apiManager.getApiByKind(evt.object.kind, evt.object.apiVersion);

View File

@ -1,7 +1,7 @@
import { action, comparer, observable, reaction } from "mobx"; import { action, comparer, observable, reaction } from "mobx";
import { autobind, createStorage } from "../../utils"; import { autobind, createStorage } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store"; import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { Namespace, namespacesApi } from "../../api/endpoints"; import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api";
import { createPageParam } from "../../navigation"; import { createPageParam } from "../../navigation";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
import { isAllowedResource } from "../../../common/rbac"; import { isAllowedResource } from "../../../common/rbac";
@ -61,18 +61,11 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
return super.subscribe(apis); return super.subscribe(apis);
} }
protected async loadItems(namespaces?: string[]) { protected async loadItems({ isAdmin, namespaces }: KubeObjectStoreLoadingParams) {
if (!isAllowedResource("namespaces")) { if (!isAllowedResource("namespaces")) {
if (namespaces) return namespaces.map(this.getDummyNamespace); return namespaces.map(this.getDummyNamespace);
return [];
} }
if (namespaces) {
return Promise.all(namespaces.map(name => this.api.get({ name }))); return Promise.all(namespaces.map(name => this.api.get({ name })));
} else {
return super.loadItems();
}
} }
protected getDummyNamespace(name: string) { protected getDummyNamespace(name: string) {
@ -105,12 +98,6 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
else this.contextNs.push(namespace); else this.contextNs.push(namespace);
} }
@action
reset() {
super.reset();
this.contextNs.clear();
}
@action @action
async remove(item: Namespace) { async remove(item: Namespace) {
await super.remove(item); await super.remove(item);

View File

@ -1,7 +1,7 @@
import difference from "lodash/difference"; import difference from "lodash/difference";
import uniqBy from "lodash/uniqBy"; import uniqBy from "lodash/uniqBy";
import { clusterRoleBindingApi, IRoleBindingSubject, RoleBinding, roleBindingApi } from "../../api/endpoints"; import { clusterRoleBindingApi, IRoleBindingSubject, RoleBinding, roleBindingApi } from "../../api/endpoints";
import { KubeObjectStore } from "../../kube-object.store"; import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { autobind } from "../../utils"; import { autobind } from "../../utils";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
@ -26,15 +26,12 @@ export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
return clusterRoleBindingApi.get(params); return clusterRoleBindingApi.get(params);
} }
protected loadItems(namespaces?: string[]) { protected async loadItems({ isAdmin, namespaces }: KubeObjectStoreLoadingParams): Promise<RoleBinding[]> {
if (namespaces) { const items = await Promise.all([
return Promise.all( super.loadItems({ isAdmin, namespaces, api: clusterRoleBindingApi }),
namespaces.map(namespace => roleBindingApi.list({ namespace })) super.loadItems({ isAdmin, namespaces, api: roleBindingApi }),
).then(items => items.flat()); ]);
} else { return items.flat();
return Promise.all([clusterRoleBindingApi.list(), roleBindingApi.list()])
.then(items => items.flat());
}
} }
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<RoleBinding>) { protected async createItem(params: { name: string; namespace?: string }, data?: Partial<RoleBinding>) {

View File

@ -1,6 +1,6 @@
import { clusterRoleApi, Role, roleApi } from "../../api/endpoints"; import { clusterRoleApi, Role, roleApi } from "../../api/endpoints";
import { autobind } from "../../utils"; import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store"; import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
@autobind() @autobind()
@ -24,15 +24,12 @@ export class RolesStore extends KubeObjectStore<Role> {
return clusterRoleApi.get(params); return clusterRoleApi.get(params);
} }
protected loadItems(namespaces?: string[]): Promise<Role[]> { protected async loadItems({ isAdmin, namespaces }: KubeObjectStoreLoadingParams): Promise<Role[]> {
if (namespaces) { const items = await Promise.all([
return Promise.all( super.loadItems({ isAdmin, namespaces, api: clusterRoleApi }),
namespaces.map(namespace => roleApi.list({ namespace })) super.loadItems({ isAdmin, namespaces, api: roleApi }),
).then(items => items.flat()); ]);
} else { return items.flat();
return Promise.all([clusterRoleApi.list(), roleApi.list()])
.then(items => items.flat());
}
} }
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<Role>) { protected async createItem(params: { name: string; namespace?: string }, data?: Partial<Role>) {

View File

@ -8,6 +8,12 @@ import { IKubeApiQueryParams, KubeApi } from "./api/kube-api";
import { KubeJsonApiData } from "./api/kube-json-api"; import { KubeJsonApiData } from "./api/kube-json-api";
import { getHostedCluster } from "../common/cluster-store"; import { getHostedCluster } from "../common/cluster-store";
export interface KubeObjectStoreLoadingParams {
isAdmin: boolean;
namespaces: string[];
api?: KubeApi;
}
@autobind() @autobind()
export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemStore<T> { export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemStore<T> {
abstract api: KubeApi<T>; abstract api: KubeApi<T>;
@ -71,14 +77,15 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
} }
protected async loadItems(allowedNamespaces?: string[]): Promise<T[]> { protected async loadItems({ isAdmin, namespaces, api }: KubeObjectStoreLoadingParams): Promise<T[]> {
if (!this.api.isNamespaced || !allowedNamespaces) { if (!api.isNamespaced) {
return this.api.list({}, this.query); if (isAdmin) return api.list({}, this.query);
} else { return [];
return Promise
.all(allowedNamespaces.map(namespace => this.api.list({ namespace })))
.then(items => items.flat());
} }
return Promise
.all(namespaces.map(namespace => api.list({ namespace })))
.then(items => items.flat());
} }
protected filterItemsOnLoad(items: T[]) { protected filterItemsOnLoad(items: T[]) {
@ -91,22 +98,16 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
let items: T[]; let items: T[];
try { try {
const { allowedNamespaces, accessibleNamespaces, isAdmin } = getHostedCluster(); const { allowedNamespaces: namespaces, isAdmin } = getHostedCluster();
items = await this.loadItems({ isAdmin, namespaces, api: this.api });
if (isAdmin && accessibleNamespaces.length == 0) {
items = await this.loadItems();
} else {
items = await this.loadItems(allowedNamespaces);
}
items = this.filterItemsOnLoad(items); items = this.filterItemsOnLoad(items);
} finally {
if (items) {
items = this.sortItems(items); items = this.sortItems(items);
this.items.replace(items); this.items.replace(items);
}
this.isLoading = false;
this.isLoaded = true; this.isLoaded = true;
} catch (error) {
console.error("Loading store items failed", { error, store: this });
} finally {
this.isLoading = false;
} }
} }