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

Fix getting allowed resources, don't display workloads overview if no resources are allowed

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-06-17 09:53:39 -04:00
parent 55d3e996cb
commit 439c469ce6
4 changed files with 32 additions and 24 deletions

View File

@ -519,20 +519,25 @@ export class Cluster implements ClusterModel, ClusterState {
return getClusterResources(await this.getProxyKubeconfig()); return getClusterResources(await this.getProxyKubeconfig());
}, 60 * 1000); // 1min }, 60 * 1000); // 1min
private isAllowedCheckers = new ExtendedObservableMap<string, () => Promise<Map<string, boolean>>>(); private isAllowedCheckers = new ExtendedObservableMap<string, () => Promise<Set<string>>>();
private async getIsAllowedResourcesInNamespace(namespace: string): Promise<Map<string, boolean>> { private async getIsAllowedResourcesInNamespace(namespace: string): Promise<Set<string>> {
console.log("getIsAllowedResourcesInNamespace", Date.now());
const groups = await this.getApiResourceMap(); const groups = await this.getApiResourceMap();
const isAllowed = new Map<string, boolean>(); const isAllowed = new Set<string>();
for (const group of groups.values()) { for (const group of groups.values()) {
for (const versions of group.values()) { for (const versions of group.values()) {
for (const resource of versions.keys()) { for (const resource of versions.keys()) {
isAllowed.set(resource, await this.canI({ const canList = await this.canI({
name: resource, resource,
namespace, namespace,
verb: "list", verb: "list",
})); });
if (canList) {
isAllowed.add(resource);
}
} }
} }
} }
@ -540,7 +545,7 @@ export class Cluster implements ClusterModel, ClusterState {
return isAllowed; return isAllowed;
} }
async getIsAllowedResources(namespace: string): Promise<Map<string, boolean>> { async getIsAllowedResources(namespace: string): Promise<Set<string>> {
return this.isAllowedCheckers.getOrInsert( return this.isAllowedCheckers.getOrInsert(
namespace, namespace,
() => asyncThrottle( () => asyncThrottle(
@ -566,7 +571,7 @@ export class Cluster implements ClusterModel, ClusterState {
const accessReview = await this.canIApiLimit(() => authApi.createSelfSubjectAccessReview({ const accessReview = await this.canIApiLimit(() => authApi.createSelfSubjectAccessReview({
apiVersion: "authorization.k8s.io/v1", apiVersion: "authorization.k8s.io/v1",
kind: "SelfSubjectAccessReview", kind: "SelfSubjectAccessReview",
spec: { resourceAttributes } spec: { resourceAttributes },
})); }));
return accessReview.body.status.allowed; return accessReview.body.status.allowed;

View File

@ -119,19 +119,19 @@ export function initIpcMainHandlers() {
?.getApiResourceMap(); ?.getApiResourceMap();
}); });
ipcMainHandle(ClusterResourceIsAllowedChannel, async (event, clusterId: ClusterId, namespaces: string[]): Promise<[string, boolean][]> => { ipcMainHandle(ClusterResourceIsAllowedChannel, async (event, clusterId: ClusterId, namespaces: string[]): Promise<string[]> => {
const cluster = ClusterStore.getInstance().getById(clusterId); const cluster = ClusterStore.getInstance().getById(clusterId);
if (!cluster) { if (!cluster) {
return []; return [];
} }
const isAllowed = new Map<string, boolean>(); const isAllowed = new Set<string>();
await Promise.all( await Promise.all(
namespaces.map(async namespace => { namespaces.map(async namespace => {
for (const [resource, canList] of await cluster.getIsAllowedResources(namespace)) { for (const resource of await cluster.getIsAllowedResources(namespace)) {
isAllowed.set(resource, Boolean(isAllowed.get(resource)) || canList); isAllowed.add(resource);
} }
}) })
); );

View File

@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { action, makeObservable, observable, ObservableMap, reaction } from "mobx"; import { action, makeObservable, observable, reaction } from "mobx";
import type { ClusterId } from "../../common/cluster-store"; import type { ClusterId } from "../../common/cluster-store";
import { ClusterResourceIsAllowedChannel, ClusterGetResourcesChannel, requestMain } from "../../common/ipc"; import { ClusterResourceIsAllowedChannel, ClusterGetResourcesChannel, requestMain } from "../../common/ipc";
import { Disposer, Singleton } from "../utils"; import { Disposer, Singleton } from "../utils";
@ -31,7 +31,7 @@ type NamespaceName = string;
type ResourceName = string; type ResourceName = string;
export class AllowedResources extends Singleton { export class AllowedResources extends Singleton {
protected allowedResourceMap = new ObservableMap<ResourceName, boolean>(); protected allowedResources = observable.set<ResourceName>();
@observable public resources: ApiResourceMap; @observable public resources: ApiResourceMap;
/** /**
@ -65,9 +65,10 @@ export class AllowedResources extends Singleton {
); );
} }
@action
private async refresh(namespaces: NamespaceName[]) { private async refresh(namespaces: NamespaceName[]) {
try { try {
this.allowedResourceMap.replace(await requestMain(ClusterResourceIsAllowedChannel, this.clusterId, namespaces)); this.allowedResources.replace(await requestMain(ClusterResourceIsAllowedChannel, this.clusterId, namespaces));
} catch (error) { } catch (error) {
console.error("[ALLOWED-RESOURCES]: failed to refresh", error, { namespaces }); console.error("[ALLOWED-RESOURCES]: failed to refresh", error, { namespaces });
Notifications.error("Failed to refresh allowed resources"); Notifications.error("Failed to refresh allowed resources");
@ -81,7 +82,7 @@ export class AllowedResources extends Singleton {
* @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 * @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 { isAllowed(name: ResourceName): boolean {
return this.allowedResourceMap.get(name) ?? false; return this.allowedResources.has(name);
} }
} }

View File

@ -39,14 +39,7 @@ import * as routes from "../../../common/routes";
@observer @observer
export class Workloads extends React.Component { export class Workloads extends React.Component {
@computed static get tabRoutes(): TabLayoutRoute[] { @computed static get tabRoutes(): TabLayoutRoute[] {
const tabs: TabLayoutRoute[] = [ const tabs: TabLayoutRoute[] = [];
{
title: "Overview",
component: WorkloadsOverview,
url: routes.overviewURL(),
routePath: routes.overviewRoute.path.toString()
}
];
if (isAllowedResource("pods")) { if (isAllowedResource("pods")) {
tabs.push({ tabs.push({
@ -111,6 +104,15 @@ export class Workloads extends React.Component {
}); });
} }
if (tabs.length > 0) {
tabs.unshift({
title: "Overview",
component: WorkloadsOverview,
url: routes.overviewURL(),
routePath: routes.overviewRoute.path.toString()
});
}
return tabs; return tabs;
} }