mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add behavioural tests for Cluster Menu K8s Resources in Sidebar menu not being shown (#7280)
* Add behavioural tests to cover bug fix Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove previous fix to fix last test Signed-off-by: Sebastian Malton <sebastian@malton.name> * More consistent impl of flushPromises Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove ContextHandler test (dead code) Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix PrometheusHandler describe text Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add useful case test-utils helper Signed-off-by: Sebastian Malton <sebastian@malton.name> * Rename file to match token Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup tests to fix type errors and use tables Signed-off-by: Sebastian Malton <sebastian@malton.name> --------- Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
5409324236
commit
8a80607d85
32 files changed
+1104
-774
No files matched your search
+5
-2
@@ -15,8 +15,11 @@ export interface ClusterData {
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
export type RequestApiVersions = (cluster: ClusterData) => AsyncResult<KubeResourceListGroup[], Error>;
|
||||
export interface ApiVersionsRequester {
|
||||
request(cluster: ClusterData): AsyncResult<KubeResourceListGroup[], Error>;
|
||||
readonly orderNumber: number;
|
||||
}
|
||||
|
||||
export const requestApiVersionsInjectionToken = getInjectionToken<RequestApiVersions>({
|
||||
export const apiVersionsRequesterInjectionToken = getInjectionToken<ApiVersionsRequester>({
|
||||
id: "request-api-versions-token",
|
||||
});
|
||||
@@ -6,10 +6,7 @@
|
||||
import { type KubeConfig, HttpError } from "@kubernetes/client-node";
|
||||
import { reaction, comparer, runInAction } from "mobx";
|
||||
import { ClusterStatus } from "../../common/cluster-types";
|
||||
import type { CreateAuthorizationReview } from "../../common/cluster/authorization-review.injectable";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { CreateListNamespaces } from "../../common/cluster/list-namespaces.injectable";
|
||||
import type { RequestNamespaceListPermissionsFor, RequestNamespaceListPermissions } from "../../common/cluster/request-namespace-list-permissions.injectable";
|
||||
import type { BroadcastMessage } from "../../common/ipc/broadcast-message.injectable";
|
||||
import { clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster";
|
||||
import type { Logger } from "../../common/logger";
|
||||
@@ -25,7 +22,6 @@ import type { RequestApiResources } from "./request-api-resources.injectable";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import broadcastConnectionUpdateInjectable from "./broadcast-connection-update.injectable";
|
||||
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
|
||||
import createAuthorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
||||
import createListNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
||||
import kubeAuthProxyServerInjectable from "./kube-auth-proxy-server.injectable";
|
||||
import loadProxyKubeconfigInjectable from "./load-proxy-kubeconfig.injectable";
|
||||
@@ -33,21 +29,31 @@ import loggerInjectable from "../../common/logger.injectable";
|
||||
import prometheusHandlerInjectable from "./prometheus-handler/prometheus-handler.injectable";
|
||||
import removeProxyKubeconfigInjectable from "./remove-proxy-kubeconfig.injectable";
|
||||
import requestApiResourcesInjectable from "./request-api-resources.injectable";
|
||||
import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
|
||||
import type { DetectClusterMetadata } from "../cluster-detectors/detect-cluster-metadata.injectable";
|
||||
import type { FallibleOnlyClusterMetadataDetector } from "../cluster-detectors/token";
|
||||
import clusterVersionDetectorInjectable from "../cluster-detectors/cluster-version-detector.injectable";
|
||||
import detectClusterMetadataInjectable from "../cluster-detectors/detect-cluster-metadata.injectable";
|
||||
import { replaceObservableObject } from "../../common/utils/replace-observable-object";
|
||||
import type { CreateAuthorizationApi } from "../../common/cluster/create-authorization-api.injectable";
|
||||
import type { CreateCanI } from "../../common/cluster/create-can-i.injectable";
|
||||
import type { CreateRequestNamespaceListPermissions, RequestNamespaceListPermissions } from "../../common/cluster/create-request-namespace-list-permissions.injectable";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import createAuthorizationApiInjectable from "../../common/cluster/create-authorization-api.injectable";
|
||||
import createCanIInjectable from "../../common/cluster/create-can-i.injectable";
|
||||
import createRequestNamespaceListPermissionsInjectable from "../../common/cluster/create-request-namespace-list-permissions.injectable";
|
||||
import type { CreateCoreApi } from "../../common/cluster/create-core-api.injectable";
|
||||
import createCoreApiInjectable from "../../common/cluster/create-core-api.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
readonly logger: Logger;
|
||||
readonly prometheusHandler: ClusterPrometheusHandler;
|
||||
readonly kubeAuthProxyServer: KubeAuthProxyServer;
|
||||
readonly clusterVersionDetector: FallibleOnlyClusterMetadataDetector;
|
||||
createAuthorizationReview: CreateAuthorizationReview;
|
||||
createCanI: CreateCanI;
|
||||
requestApiResources: RequestApiResources;
|
||||
requestNamespaceListPermissionsFor: RequestNamespaceListPermissionsFor;
|
||||
createRequestNamespaceListPermissions: CreateRequestNamespaceListPermissions;
|
||||
createAuthorizationApi: CreateAuthorizationApi;
|
||||
createCoreApi: CreateCoreApi;
|
||||
createListNamespaces: CreateListNamespaces;
|
||||
detectClusterMetadata: DetectClusterMetadata;
|
||||
broadcastMessage: BroadcastMessage;
|
||||
@@ -224,8 +230,9 @@ class ClusterConnection {
|
||||
private async refreshAccessibility(): Promise<void> {
|
||||
this.dependencies.logger.info(`[CLUSTER]: refreshAccessibility`, this.cluster.getMeta());
|
||||
const proxyConfig = await this.dependencies.loadProxyKubeconfig();
|
||||
const canI = this.dependencies.createAuthorizationReview(proxyConfig);
|
||||
const requestNamespaceListPermissions = this.dependencies.requestNamespaceListPermissionsFor(proxyConfig);
|
||||
const api = this.dependencies.createAuthorizationApi(proxyConfig);
|
||||
const canI = this.dependencies.createCanI(api);
|
||||
const requestNamespaceListPermissions = this.dependencies.createRequestNamespaceListPermissions(api);
|
||||
|
||||
const isAdmin = await canI({
|
||||
namespace: "kube-system",
|
||||
@@ -360,7 +367,8 @@ class ClusterConnection {
|
||||
}
|
||||
|
||||
try {
|
||||
const listNamespaces = this.dependencies.createListNamespaces(proxyConfig);
|
||||
const api = this.dependencies.createCoreApi(proxyConfig);
|
||||
const listNamespaces = this.dependencies.createListNamespaces(api);
|
||||
|
||||
return await listNamespaces();
|
||||
} catch (error) {
|
||||
@@ -403,13 +411,15 @@ const clusterConnectionInjectable = getInjectable({
|
||||
prometheusHandler: di.inject(prometheusHandlerInjectable, cluster),
|
||||
broadcastConnectionUpdate: di.inject(broadcastConnectionUpdateInjectable, cluster),
|
||||
broadcastMessage: di.inject(broadcastMessageInjectable),
|
||||
createAuthorizationReview: di.inject(createAuthorizationReviewInjectable),
|
||||
createListNamespaces: di.inject(createListNamespacesInjectable),
|
||||
detectClusterMetadata: di.inject(detectClusterMetadataInjectable),
|
||||
loadProxyKubeconfig: di.inject(loadProxyKubeconfigInjectable, cluster),
|
||||
removeProxyKubeconfig: di.inject(removeProxyKubeconfigInjectable, cluster),
|
||||
requestApiResources: di.inject(requestApiResourcesInjectable),
|
||||
requestNamespaceListPermissionsFor: di.inject(requestNamespaceListPermissionsForInjectable),
|
||||
createAuthorizationApi: di.inject(createAuthorizationApiInjectable),
|
||||
createCoreApi: di.inject(createCoreApiInjectable),
|
||||
createCanI: di.inject(createCanIInjectable),
|
||||
createRequestNamespaceListPermissions: di.inject(createRequestNamespaceListPermissionsInjectable),
|
||||
},
|
||||
cluster,
|
||||
),
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||
import kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
|
||||
import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
|
||||
import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||
|
||||
export interface KubeAuthProxyServer {
|
||||
getApiTarget(isLongRunningRequest?: boolean): Promise<ServerOptions>;
|
||||
|
||||
@@ -7,11 +7,12 @@ import { getInjectable } from "@ogre-tools/injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import type { KubeApiResource } from "../../common/rbac";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { requestApiVersionsInjectionToken } from "./request-api-versions";
|
||||
import { apiVersionsRequesterInjectionToken } from "./api-versions-requester";
|
||||
import { backoffCaller, withConcurrencyLimit } from "@k8slens/utilities";
|
||||
import requestKubeApiResourcesForInjectable from "./request-kube-api-resources-for.injectable";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import broadcastConnectionUpdateInjectable from "./broadcast-connection-update.injectable";
|
||||
import { byOrderNumber } from "../../common/utils/composable-responsibilities/orderable/orderable";
|
||||
|
||||
export type RequestApiResources = (cluster: Cluster) => AsyncResult<KubeApiResource[], Error>;
|
||||
|
||||
@@ -24,7 +25,8 @@ const requestApiResourcesInjectable = getInjectable({
|
||||
id: "request-api-resources",
|
||||
instantiate: (di): RequestApiResources => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const apiVersionRequesters = di.injectMany(requestApiVersionsInjectionToken);
|
||||
const apiVersionRequesters = di.injectMany(apiVersionsRequesterInjectionToken)
|
||||
.sort(byOrderNumber);
|
||||
const requestKubeApiResourcesFor = di.inject(requestKubeApiResourcesForInjectable);
|
||||
|
||||
return async (...args) => {
|
||||
@@ -35,7 +37,7 @@ const requestApiResourcesInjectable = getInjectable({
|
||||
const groupLists: KubeResourceListGroup[] = [];
|
||||
|
||||
for (const apiVersionRequester of apiVersionRequesters) {
|
||||
const result = await backoffCaller(() => apiVersionRequester(cluster), {
|
||||
const result = await backoffCaller(() => apiVersionRequester.request(cluster), {
|
||||
onIntermediateError: (error, attempt) => {
|
||||
broadcastConnectionUpdate({
|
||||
message: `Failed to list kube API resource kinds, attempt ${attempt}: ${error}`,
|
||||
|
||||
@@ -5,33 +5,36 @@
|
||||
import type { V1APIVersions } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||
import { requestApiVersionsInjectionToken } from "./request-api-versions";
|
||||
import { apiVersionsRequesterInjectionToken } from "./api-versions-requester";
|
||||
|
||||
const requestCoreApiVersionsInjectable = getInjectable({
|
||||
id: "request-core-api-versions",
|
||||
instantiate: (di) => {
|
||||
const k8sRequest = di.inject(k8sRequestInjectable);
|
||||
|
||||
return async (cluster) => {
|
||||
try {
|
||||
const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
|
||||
return {
|
||||
request: async (cluster) => {
|
||||
try {
|
||||
const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
|
||||
|
||||
return {
|
||||
callWasSuccessful: true,
|
||||
response: versions.map(version => ({
|
||||
group: "",
|
||||
path: `/api/${version}`,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: error as Error,
|
||||
};
|
||||
}
|
||||
return {
|
||||
callWasSuccessful: true,
|
||||
response: versions.map(version => ({
|
||||
group: "",
|
||||
path: `/api/${version}`,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: error as Error,
|
||||
};
|
||||
}
|
||||
},
|
||||
orderNumber: 10,
|
||||
};
|
||||
},
|
||||
injectionToken: requestApiVersionsInjectionToken,
|
||||
injectionToken: apiVersionsRequesterInjectionToken,
|
||||
});
|
||||
|
||||
export default requestCoreApiVersionsInjectable;
|
||||
@@ -8,7 +8,7 @@ import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { KubeApiResource } from "../../common/rbac";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||
import type { KubeResourceListGroup } from "./request-api-versions";
|
||||
import type { KubeResourceListGroup } from "./api-versions-requester";
|
||||
|
||||
export type RequestKubeApiResources = (grouping: KubeResourceListGroup) => AsyncResult<KubeApiResource[], Error>;
|
||||
|
||||
|
||||
@@ -6,35 +6,40 @@ import type { V1APIGroupList } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { iter } from "@k8slens/utilities";
|
||||
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||
import { requestApiVersionsInjectionToken } from "./request-api-versions";
|
||||
import { apiVersionsRequesterInjectionToken } from "./api-versions-requester";
|
||||
|
||||
const requestNonCoreApiVersionsInjectable = getInjectable({
|
||||
id: "request-non-core-api-versions",
|
||||
instantiate: (di) => {
|
||||
const k8sRequest = di.inject(k8sRequestInjectable);
|
||||
|
||||
return async (cluster) => {
|
||||
try {
|
||||
const { groups } = await k8sRequest(cluster, "/apis") as V1APIGroupList;
|
||||
return {
|
||||
request: async (cluster) => {
|
||||
try {
|
||||
const { groups } = (await k8sRequest(cluster, "/apis")) as V1APIGroupList;
|
||||
|
||||
return {
|
||||
callWasSuccessful: true,
|
||||
response: iter.chain(groups.values())
|
||||
.flatMap(group => group.versions.map(version => ({
|
||||
group: group.name,
|
||||
path: `/apis/${version.groupVersion}`,
|
||||
})))
|
||||
.collect(v => [...v]),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: error as Error,
|
||||
};
|
||||
}
|
||||
return {
|
||||
callWasSuccessful: true,
|
||||
response: iter.chain(groups.values())
|
||||
.flatMap((group) =>
|
||||
group.versions.map((version) => ({
|
||||
group: group.name,
|
||||
path: `/apis/${version.groupVersion}`,
|
||||
})),
|
||||
)
|
||||
.collect((v) => [...v]),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: error as Error,
|
||||
};
|
||||
}
|
||||
},
|
||||
orderNumber: 20,
|
||||
};
|
||||
},
|
||||
injectionToken: requestApiVersionsInjectionToken,
|
||||
injectionToken: apiVersionsRequesterInjectionToken,
|
||||
});
|
||||
|
||||
export default requestNonCoreApiVersionsInjectable;
|
||||
@@ -10,13 +10,13 @@ import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import type { K8sRequest } from "../k8s-request.injectable";
|
||||
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||
import type { RequestApiVersions } from "./request-api-versions";
|
||||
import type { ApiVersionsRequester } from "./api-versions-requester";
|
||||
import requestNonCoreApiVersionsInjectable from "./request-non-core-api-versions.injectable";
|
||||
|
||||
describe("requestNonCoreApiVersions", () => {
|
||||
let di: DiContainer;
|
||||
let k8sRequestMock: AsyncFnMock<K8sRequest>;
|
||||
let requestNonCoreApiVersions: RequestApiVersions;
|
||||
let requestNonCoreApiVersions: ApiVersionsRequester;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
@@ -28,10 +28,10 @@ describe("requestNonCoreApiVersions", () => {
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let versionsRequest: ReturnType<RequestApiVersions>;
|
||||
let versionsRequest: ReturnType<ApiVersionsRequester["request"]>;
|
||||
|
||||
beforeEach(() => {
|
||||
versionsRequest = requestNonCoreApiVersions({ id: "some-cluster-id" });
|
||||
versionsRequest = requestNonCoreApiVersions.request({ id: "some-cluster-id" });
|
||||
});
|
||||
|
||||
it("should request all api groups", () => {
|
||||
|
||||
Reference in new issue
Block a user