mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
refactoring: get rid of config.store.ts (renderer), moved allowNamespaces/allowedResources to cluster.ts apis
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
1 parent
407c4fb4d0
commit
3aef3700de
19 files changed
+158
-281
No files matched your search
+100
-18
@@ -1,7 +1,7 @@
|
||||
import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/cluster-store"
|
||||
import type { FeatureStatusMap } from "./feature"
|
||||
import type { WorkspaceId } from "../common/workspace-store";
|
||||
import { action, computed, observable, reaction, toJS, when } from "mobx";
|
||||
import { action, computed, observable, reaction, toJS } from "mobx";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import { broadcastIpc } from "../common/ipc";
|
||||
import { ContextHandler } from "./context-handler"
|
||||
@@ -20,17 +20,19 @@ export enum ClusterStatus {
|
||||
}
|
||||
|
||||
export interface ClusterState extends ClusterModel {
|
||||
initialized?: boolean;
|
||||
initialized: boolean;
|
||||
apiUrl: string;
|
||||
online?: boolean;
|
||||
accessible?: boolean;
|
||||
failureReason?: string;
|
||||
nodes?: number;
|
||||
eventCount?: number;
|
||||
version?: string;
|
||||
distribution?: string;
|
||||
isAdmin?: boolean;
|
||||
features?: FeatureStatusMap;
|
||||
online: boolean;
|
||||
accessible: boolean;
|
||||
failureReason: string;
|
||||
nodes: number;
|
||||
eventCount: number;
|
||||
version: string;
|
||||
distribution: string;
|
||||
isAdmin: boolean;
|
||||
allowedNamespaces: string[]
|
||||
allowedResources: string[]
|
||||
features: FeatureStatusMap;
|
||||
}
|
||||
|
||||
export class Cluster implements ClusterModel {
|
||||
@@ -58,6 +60,8 @@ export class Cluster implements ClusterModel {
|
||||
@observable eventCount = 0;
|
||||
@observable preferences: ClusterPreferences = {};
|
||||
@observable features: FeatureStatusMap = {};
|
||||
@observable allowedNamespaces: string[] = [];
|
||||
@observable allowedResources: string[] = [];
|
||||
|
||||
constructor(model: ClusterModel) {
|
||||
this.updateModel(model);
|
||||
@@ -120,7 +124,6 @@ export class Cluster implements ClusterModel {
|
||||
}
|
||||
|
||||
async activate() {
|
||||
await when(() => this.initialized);
|
||||
if (this.disconnected) await this.reconnect();
|
||||
await this.refresh();
|
||||
return this.pushState();
|
||||
@@ -150,14 +153,28 @@ export class Cluster implements ClusterModel {
|
||||
this.online = connectionStatus > ClusterStatus.Offline;
|
||||
this.accessible = connectionStatus == ClusterStatus.AccessGranted;
|
||||
if (this.accessible) {
|
||||
this.distribution = this.detectKubernetesDistribution(this.version)
|
||||
this.features = await getFeatures(this)
|
||||
this.isAdmin = await this.isClusterAdmin()
|
||||
this.nodes = await this.getNodeCount()
|
||||
this.kubeCtl = new Kubectl(this.version)
|
||||
this.kubeCtl.ensureKubectl()
|
||||
this.distribution = this.detectKubernetesDistribution(this.version)
|
||||
const [features, isAdmin, nodesCount] = await Promise.all([
|
||||
getFeatures(this),
|
||||
this.isClusterAdmin(),
|
||||
this.getNodeCount(),
|
||||
this.kubeCtl.ensureKubectl()
|
||||
]);
|
||||
this.features = features;
|
||||
this.isAdmin = isAdmin;
|
||||
this.nodes = nodesCount;
|
||||
}
|
||||
await this.refreshEvents();
|
||||
await Promise.all([
|
||||
this.refreshEvents(),
|
||||
this.refreshAllowedResources(),
|
||||
]);
|
||||
}
|
||||
|
||||
@action
|
||||
async refreshAllowedResources() {
|
||||
this.allowedNamespaces = await this.getAllowedNamespaces();
|
||||
this.allowedResources = await this.getAllowedResources();
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -342,6 +359,8 @@ export class Cluster implements ClusterModel {
|
||||
isAdmin: this.isAdmin,
|
||||
features: this.features,
|
||||
eventCount: this.eventCount,
|
||||
allowedNamespaces: this.allowedNamespaces,
|
||||
allowedResources: this.allowedResources,
|
||||
};
|
||||
return toJS(state, {
|
||||
recurseEverything: true
|
||||
@@ -368,4 +387,67 @@ export class Cluster implements ClusterModel {
|
||||
online: this.online,
|
||||
}
|
||||
}
|
||||
|
||||
protected async getAllowedNamespaces() {
|
||||
const api = this.getProxyKubeconfig().makeApiClient(CoreV1Api)
|
||||
try {
|
||||
const namespaceList = await api.listNamespace()
|
||||
const nsAccessStatuses = await Promise.all(
|
||||
namespaceList.body.items.map(ns => this.canI({
|
||||
namespace: ns.metadata.name,
|
||||
resource: "pods",
|
||||
verb: "list",
|
||||
}))
|
||||
)
|
||||
return namespaceList.body.items
|
||||
.filter((ns, i) => nsAccessStatuses[i])
|
||||
.map(ns => ns.metadata.name)
|
||||
} catch (error) {
|
||||
const ctx = this.getProxyKubeconfig().getContextObject(this.contextName)
|
||||
if (ctx.namespace) return [ctx.namespace]
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
protected async getAllowedResources() {
|
||||
// TODO: auto-populate all resources dynamically
|
||||
const apiResources = [
|
||||
{ resource: "configmaps" },
|
||||
{ resource: "cronjobs", group: "batch" },
|
||||
{ resource: "customresourcedefinitions", group: "apiextensions.k8s.io" },
|
||||
{ resource: "daemonsets", group: "apps" },
|
||||
{ resource: "deployments", group: "apps" },
|
||||
{ resource: "endpoints" },
|
||||
{ resource: "events" },
|
||||
{ resource: "horizontalpodautoscalers" },
|
||||
{ resource: "ingresses", group: "networking.k8s.io" },
|
||||
{ resource: "jobs", group: "batch" },
|
||||
{ resource: "namespaces" },
|
||||
{ resource: "networkpolicies", group: "networking.k8s.io" },
|
||||
{ resource: "nodes" },
|
||||
{ resource: "persistentvolumes" },
|
||||
{ resource: "pods" },
|
||||
{ resource: "podsecuritypolicies" },
|
||||
{ resource: "resourcequotas" },
|
||||
{ resource: "secrets" },
|
||||
{ resource: "services" },
|
||||
{ resource: "statefulsets", group: "apps" },
|
||||
{ resource: "storageclasses", group: "storage.k8s.io" },
|
||||
]
|
||||
try {
|
||||
const resourceAccessStatuses = await Promise.all(
|
||||
apiResources.map(apiResource => this.canI({
|
||||
resource: apiResource.resource,
|
||||
group: apiResource.group,
|
||||
verb: "list",
|
||||
namespace: this.allowedNamespaces[0]
|
||||
}))
|
||||
)
|
||||
return apiResources
|
||||
.filter((resource, i) => resourceAccessStatuses[i])
|
||||
.map(apiResource => apiResource.resource)
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -5,7 +5,7 @@ import path from "path"
|
||||
import { readFile } from "fs-extra"
|
||||
import { Cluster } from "./cluster"
|
||||
import { apiPrefix, appName, outDir } from "../common/vars";
|
||||
import { configRoute, helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
||||
import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
||||
|
||||
export interface RouterRequestOpts {
|
||||
req: http.IncomingMessage;
|
||||
@@ -112,7 +112,6 @@ export class Router {
|
||||
this.handleStaticFile(params.path, response);
|
||||
});
|
||||
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/config` }, configRoute.routeConfig.bind(configRoute))
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute))
|
||||
|
||||
// Watch API
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import { app } from "electron"
|
||||
import { CoreV1Api } from "@kubernetes/client-node"
|
||||
import { LensApiRequest } from "../router"
|
||||
import { LensApi } from "../lens-api"
|
||||
import { Cluster } from "../cluster"
|
||||
|
||||
export interface IConfigRoutePayload {
|
||||
kubeVersion?: string;
|
||||
clusterName?: string;
|
||||
lensVersion?: string;
|
||||
username?: string;
|
||||
token?: string;
|
||||
allowedNamespaces?: string[];
|
||||
allowedResources?: string[];
|
||||
isClusterAdmin?: boolean;
|
||||
chartsEnabled: boolean;
|
||||
kubectlAccess?: boolean; // User accessed via kubectl-lens plugin
|
||||
}
|
||||
|
||||
// TODO: auto-populate all resources dynamically
|
||||
const apiResources = [
|
||||
{ resource: "configmaps" },
|
||||
{ resource: "cronjobs", group: "batch" },
|
||||
{ resource: "customresourcedefinitions", group: "apiextensions.k8s.io" },
|
||||
{ resource: "daemonsets", group: "apps" },
|
||||
{ resource: "deployments", group: "apps" },
|
||||
{ resource: "endpoints" },
|
||||
{ resource: "events" },
|
||||
{ resource: "horizontalpodautoscalers" },
|
||||
{ resource: "ingresses", group: "networking.k8s.io" },
|
||||
{ resource: "jobs", group: "batch" },
|
||||
{ resource: "namespaces" },
|
||||
{ resource: "networkpolicies", group: "networking.k8s.io" },
|
||||
{ resource: "nodes" },
|
||||
{ resource: "persistentvolumes" },
|
||||
{ resource: "pods" },
|
||||
{ resource: "podsecuritypolicies" },
|
||||
{ resource: "resourcequotas" },
|
||||
{ resource: "secrets" },
|
||||
{ resource: "services" },
|
||||
{ resource: "statefulsets", group: "apps" },
|
||||
{ resource: "storageclasses", group: "storage.k8s.io" },
|
||||
]
|
||||
|
||||
async function getAllowedNamespaces(cluster: Cluster) {
|
||||
const api = cluster.getProxyKubeconfig().makeApiClient(CoreV1Api)
|
||||
try {
|
||||
const namespaceList = await api.listNamespace()
|
||||
const nsAccessStatuses = await Promise.all(
|
||||
namespaceList.body.items.map(ns => cluster.canI({
|
||||
namespace: ns.metadata.name,
|
||||
resource: "pods",
|
||||
verb: "list",
|
||||
}))
|
||||
)
|
||||
return namespaceList.body.items
|
||||
.filter((ns, i) => nsAccessStatuses[i])
|
||||
.map(ns => ns.metadata.name)
|
||||
} catch (error) {
|
||||
const ctx = cluster.getProxyKubeconfig().getContextObject(cluster.contextName)
|
||||
if (ctx.namespace) {
|
||||
return [ctx.namespace]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllowedResources(cluster: Cluster, namespaces: string[]) {
|
||||
try {
|
||||
const resourceAccessStatuses = await Promise.all(
|
||||
apiResources.map(apiResource => cluster.canI({
|
||||
resource: apiResource.resource,
|
||||
group: apiResource.group,
|
||||
verb: "list",
|
||||
namespace: namespaces[0]
|
||||
}))
|
||||
)
|
||||
return apiResources
|
||||
.filter((resource, i) => resourceAccessStatuses[i]).map(apiResource => apiResource.resource)
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigRoute extends LensApi {
|
||||
public async routeConfig(request: LensApiRequest) {
|
||||
const { params, response, cluster } = request
|
||||
|
||||
const namespaces = await getAllowedNamespaces(cluster)
|
||||
const data: IConfigRoutePayload = {
|
||||
clusterName: cluster.contextName,
|
||||
lensVersion: app.getVersion(),
|
||||
kubeVersion: cluster.version,
|
||||
chartsEnabled: true,
|
||||
isClusterAdmin: cluster.isAdmin,
|
||||
allowedResources: await getAllowedResources(cluster, namespaces),
|
||||
allowedNamespaces: namespaces
|
||||
};
|
||||
|
||||
this.respondJson(response, data)
|
||||
}
|
||||
}
|
||||
|
||||
export const configRoute = new ConfigRoute()
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from "./config-route"
|
||||
export * from "./kubeconfig-route"
|
||||
export * from "./metrics-route"
|
||||
export * from "./port-forward-route"
|
||||
|
||||
Reference in new issue
Block a user