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:
Sebastian Malton authored and GitHub committed 2023-03-21 22:00:12 -04:00
1 parent 5409324236
commit 8a80607d85
32 files changed
+1104 -774

No files matched your search

@@ -5,10 +5,6 @@
import { Cluster } from "../../common/cluster/cluster";
import { Kubectl } from "../kubectl/kubectl";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import createAuthorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
import createListNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable";
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
@@ -19,6 +15,10 @@ import clusterConnectionInjectable from "../cluster/cluster-connection.injectabl
import kubeconfigManagerInjectable from "../kubeconfig-manager/kubeconfig-manager.injectable";
import type { KubeconfigManager } from "../kubeconfig-manager/kubeconfig-manager";
import broadcastConnectionUpdateInjectable from "../cluster/broadcast-connection-update.injectable";
import createCanIInjectable from "../../common/cluster/create-can-i.injectable";
import createRequestNamespaceListPermissionsInjectable from "../../common/cluster/create-request-namespace-list-permissions.injectable";
import createListNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable";
describe("create clusters", () => {
let cluster: Cluster;
@@ -34,8 +34,8 @@ describe("create clusters", () => {
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
di.override(broadcastConnectionUpdateInjectable, () => async () => {});
di.override(createAuthorizationReviewInjectable, () => () => () => Promise.resolve(true));
di.override(requestNamespaceListPermissionsForInjectable, () => () => async () => () => true);
di.override(createCanIInjectable, () => () => () => Promise.resolve(true));
di.override(createRequestNamespaceListPermissionsInjectable, () => () => async () => () => true);
di.override(createListNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
di.override(prometheusHandlerInjectable, () => ({
getPrometheusDetails: jest.fn(),
@@ -1,203 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import { Cluster } from "../../common/cluster/cluster";
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import type { PrometheusProvider } from "../prometheus/provider";
import { prometheusProviderInjectionToken } from "../prometheus/provider";
import { runInAction } from "mobx";
import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable";
import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
import loadProxyKubeconfigInjectable from "../cluster/load-proxy-kubeconfig.injectable";
import type { KubeConfig } from "@kubernetes/client-node";
enum ServiceResult {
Success,
Failure,
}
const createTestPrometheusProvider = (kind: string, alwaysFail: ServiceResult): PrometheusProvider => ({
kind,
name: "TestProvider1",
isConfigurable: false,
getQuery: () => {
throw new Error("getQuery is not implemented.");
},
getPrometheusService: async () => {
switch (alwaysFail) {
case ServiceResult.Success:
return {
kind,
namespace: "default",
port: 7000,
service: "",
};
case ServiceResult.Failure:
throw new Error("does fail");
}
},
});
describe("ContextHandler", () => {
let di: DiContainer;
let cluster: Cluster;
beforeEach(() => {
di = getDiForUnitTesting();
di.override(loadProxyKubeconfigInjectable, () => async () => ({
makeApiClient: () => ({} as any),
} as Partial<KubeConfig>));
di.override(createKubeAuthProxyInjectable, () => () => ({
run: async () => {},
} as KubeAuthProxy));
di.override(directoryForTempInjectable, () => "/some-directory-for-tmp");
di.inject(lensProxyPortInjectable).set(9968);
cluster = new Cluster({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-kubeconfig-path",
}, {
clusterServerUrl: "https://some-website.com",
});
});
describe("getPrometheusService", () => {
it.each([
[0],
[1],
[2],
[3],
])("should throw after %d failure(s)", async (failures) => {
runInAction(() => {
for (let i = 0; i < failures; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-failure-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
}));
}
});
expect(() => di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails()).rejects.toThrowError();
});
it.each([
[1, 0],
[1, 1],
[1, 2],
[1, 3],
[2, 0],
[2, 1],
[2, 2],
[2, 3],
])("should pick the first provider of %d success(es) after %d failure(s)", async (successes, failures) => {
runInAction(() => {
for (let i = 0; i < failures; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-failure-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
}));
}
for (let i = 0; i < successes; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-success-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
}));
}
});
const details = await di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails();
expect(details.provider.kind === `id_failure_${failures}`);
});
it.each([
[1, 0],
[1, 1],
[1, 2],
[1, 3],
[2, 0],
[2, 1],
[2, 2],
[2, 3],
])("should pick the first provider of %d success(es) before %d failure(s)", async (successes, failures) => {
runInAction(() => {
for (let i = 0; i < failures; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-failure-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
}));
}
for (let i = 0; i < successes; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-success-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
}));
}
});
const details = await di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails();
expect(details.provider.kind === "id_failure_0");
});
it.each([
[1, 0],
[1, 1],
[1, 2],
[1, 3],
[2, 0],
[2, 1],
[2, 2],
[2, 3],
])("should pick the first provider of %d success(es) between %d failure(s)", async (successes, failures) => {
const beforeSuccesses = Math.floor(successes / 2);
runInAction(() => {
for (let i = 0; i < beforeSuccesses; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-success-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
}));
}
for (let i = 0; i < failures; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-failure-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
}));
}
for (let i = beforeSuccesses; i < successes; i += 1) {
di.register(getInjectable({
id: `test-prometheus-provider-success-${i}`,
injectionToken: prometheusProviderInjectionToken,
instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
}));
}
});
const details = await di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails();
expect(details.provider.kind === "id_success_0");
});
});
});
@@ -5,7 +5,6 @@
import waitUntilPortIsUsedInjectable from "../kube-auth-proxy/wait-until-port-is-used/wait-until-port-is-used.injectable";
import { Cluster } from "../../common/cluster/cluster";
import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
import type { ChildProcess } from "child_process";
import { Kubectl } from "../kubectl/kubectl";
import type { DeepMockProxy } from "jest-mock-extended";
@@ -13,6 +12,7 @@ import { mockDeep, mock } from "jest-mock-extended";
import type { Readable } from "stream";
import { EventEmitter } from "stream";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import type { CreateKubeAuthProxy, KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import spawnInjectable from "../child-process/spawn.injectable";
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
@@ -29,7 +29,7 @@ import getBasenameOfPathInjectable from "../../common/path/get-basename.injectab
const clusterServerUrl = "https://192.168.64.3:8443";
describe("kube auth proxy tests", () => {
let createKubeAuthProxy: (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy;
let createKubeAuthProxy: CreateKubeAuthProxy;
let spawnMock: jest.Mock;
let waitUntilPortIsUsedMock: jest.Mock;
let broadcastMessageMock: jest.Mock;
@@ -43,7 +43,7 @@ const createTestPrometheusProvider = (kind: string, alwaysFail: ServiceResult):
},
});
describe("ContextHandler", () => {
describe("PrometheusHandler", () => {
let di: DiContainer;
let cluster: Cluster;
@@ -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", () => {
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { KubeAuthProxyDependencies } from "./kube-auth-proxy";
import { KubeAuthProxy } from "./kube-auth-proxy";
import { KubeAuthProxyImpl } from "./kube-auth-proxy";
import type { Cluster } from "../../common/cluster/cluster";
import spawnInjectable from "../child-process/spawn.injectable";
import kubeAuthProxyCertificateInjectable from "./kube-auth-proxy-certificate.injectable";
@@ -15,6 +15,13 @@ import getPortFromStreamInjectable from "../utils/get-port-from-stream.injectabl
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
import broadcastConnectionUpdateInjectable from "../cluster/broadcast-connection-update.injectable";
export interface KubeAuthProxy {
readonly apiPrefix: string;
readonly port: number;
run: () => Promise<void>;
exit: () => void;
}
export type CreateKubeAuthProxy = (cluster: Cluster, env: NodeJS.ProcessEnv) => KubeAuthProxy;
const createKubeAuthProxyInjectable = getInjectable({
@@ -33,7 +40,7 @@ const createKubeAuthProxyInjectable = getInjectable({
return (cluster, env) => {
const clusterUrl = new URL(cluster.apiUrl.get());
return new KubeAuthProxy({
return new KubeAuthProxyImpl({
...dependencies,
proxyCert: di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname),
broadcastConnectionUpdate: di.inject(broadcastConnectionUpdateInjectable, cluster),
@@ -16,6 +16,7 @@ import type { Logger } from "../../common/logger";
import type { WaitUntilPortIsUsed } from "./wait-until-port-is-used/wait-until-port-is-used.injectable";
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
import type { BroadcastConnectionUpdate } from "../cluster/broadcast-connection-update.injectable";
import type { KubeAuthProxy } from "./create-kube-auth-proxy.injectable";
const startingServeMatcher = "starting to serve on (?<address>.+)";
const startingServeRegex = Object.assign(TypedRegEx(startingServeMatcher, "i"), {
@@ -33,7 +34,7 @@ export interface KubeAuthProxyDependencies {
broadcastConnectionUpdate: BroadcastConnectionUpdate;
}
export class KubeAuthProxy {
export class KubeAuthProxyImpl implements KubeAuthProxy {
public readonly apiPrefix = `/${randomBytes(8).toString("hex")}`;
public get port(): number {
@@ -85,7 +85,7 @@ export class KubeconfigManager {
return this.tempFilePath = await this.createProxyKubeconfig();
} catch (error) {
throw new Error(`Failed to creat temp config for auth-proxy: ${error}`);
throw new Error(`Failed to create temp config for auth-proxy: ${error}`);
}
}