diff --git a/package.json b/package.json index 30ebd93f78..b20ce96b2e 100644 --- a/package.json +++ b/package.json @@ -239,6 +239,7 @@ "node-pty": "^0.9.0", "npm": "^6.14.8", "openid-client": "^3.15.2", + "p-limit": "^3.1.0", "path-to-regexp": "^6.1.0", "proper-lockfile": "^4.1.1", "request": "^2.88.2", diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 79e28fa9c4..b9ff62e8ac 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -11,10 +11,11 @@ import { Kubectl } from "./kubectl"; import { KubeconfigManager } from "./kubeconfig-manager"; import { loadConfig } from "../common/kube-helpers"; import request, { RequestPromiseOptions } from "request-promise-native"; -import { apiResources } from "../common/rbac"; +import { apiResources, KubeApiResource } from "../common/rbac"; import logger from "./logger"; import { VersionDetector } from "./cluster-detectors/version-detector"; import { detectorRegistry } from "./cluster-detectors/detector-registry"; +import plimit from "p-limit"; export enum ClusterStatus { AccessGranted = 2, @@ -78,6 +79,7 @@ export class Cluster implements ClusterModel, ClusterState { protected kubeconfigManager: KubeconfigManager; protected eventDisposers: Function[] = []; protected activated = false; + private resourceAccessStatuses: Map = new Map(); whenInitialized = when(() => this.initialized); whenReady = when(() => this.ready); @@ -379,6 +381,7 @@ export class Cluster implements ClusterModel, ClusterState { this.accessible = false; this.ready = false; this.activated = false; + this.resourceAccessStatuses.clear(); this.pushState(); } @@ -484,6 +487,8 @@ export class Cluster implements ClusterModel, ClusterState { this.metadata.version = versionData.value; + this.failureReason = null; + return ClusterStatus.AccessGranted; } catch (error) { logger.error(`Failed to connect cluster "${this.contextName}": ${error}`); @@ -643,17 +648,30 @@ export class Cluster implements ClusterModel, ClusterState { if (!this.allowedNamespaces.length) { return []; } - const resourceAccessStatuses = await Promise.all( - apiResources.map(apiResource => this.canI({ - resource: apiResource.resource, - group: apiResource.group, - verb: "list", - namespace: this.allowedNamespaces[0] - })) - ); + const resources = apiResources.filter((resource) => this.resourceAccessStatuses.get(resource) === undefined); + const apiLimit = plimit(5); // 5 concurrent api requests + const requests = []; + + for (const apiResource of resources) { + requests.push(apiLimit(async () => { + for (const namespace of this.allowedNamespaces.slice(0, 10)) { + if (!this.resourceAccessStatuses.get(apiResource)) { + const result = await this.canI({ + resource: apiResource.resource, + group: apiResource.group, + verb: "list", + namespace + }); + + this.resourceAccessStatuses.set(apiResource, result); + } + } + })); + } + await Promise.all(requests); return apiResources - .filter((resource, i) => resourceAccessStatuses[i]) + .filter((resource) => this.resourceAccessStatuses.get(resource)) .map(apiResource => apiResource.resource); } catch (error) { return []; diff --git a/yarn.lock b/yarn.lock index cbfe4a3ccd..aba182b040 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11194,6 +11194,13 @@ p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.3.0: dependencies: p-try "^2.0.0" +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -15557,6 +15564,11 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zip-stream@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"