Merge remote-tracking branch 'origin' into monorepo

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen committed 2023-01-13 07:42:09 +02:00
commit 3c63f90166
162 files changed
+1451 -821

No files matched your search

@@ -13,7 +13,7 @@ const configMapsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "configmaps",
group: "v1",
group: "",
}),
}),
injectionToken: frontEndRouteInjectionToken,
@@ -14,7 +14,7 @@ const limitRangesRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "limitranges",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const resourceQuotasRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "resourcequotas",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const secretsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "secrets",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const eventsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "events",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const namespacesRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "namespaces",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const endpointsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "endpoints",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const servicesRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "services",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const nodesRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "nodes",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const clusterOverviewRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "nodes",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const persistentVolumeClaimsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "persistentvolumeclaims",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const persistentVolumesRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "persistentvolumes",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const serviceAccountsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "serviceaccounts",
group: "v1",
group: "",
}),
}),
@@ -14,7 +14,7 @@ const podsRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "pods",
group: "v1",
group: "",
}),
}),
@@ -6,13 +6,11 @@ import type { ApiManager } from "../api-manager";
import type { IngressApi } from "../endpoints";
import { Ingress } from "../endpoints";
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
import apiManagerInjectable from "../api-manager/manager.injectable";
import type { Fetch } from "../../fetch/fetch.injectable";
import fetchInjectable from "../../fetch/fetch.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import { flushPromises } from "../../test-utils/flush-promises";
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable";
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
@@ -20,14 +18,14 @@ import directoryForUserDataInjectable from "../../app-paths/directory-for-user-d
import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable";
import apiManagerInjectable from "../api-manager/manager.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import ingressApiInjectable from "../endpoints/ingress.api.injectable";
describe("KubeApi", () => {
let di: DiContainer;
let registerApiSpy: jest.SpiedFunction<ApiManager["registerApi"]>;
let fetchMock: AsyncFnMock<Fetch>;
let apiManager: ApiManager;
let di: DiContainer;
beforeEach(async () => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
@@ -40,7 +38,6 @@ describe("KubeApi", () => {
di.override(storesAndApisCanBeCreatedInjectable, () => true);
const createCluster = di.inject(createClusterInjectable);
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
di.override(hostedClusterInjectable, () => createCluster({
contextName: "some-context-name",
@@ -50,12 +47,7 @@ describe("KubeApi", () => {
clusterServerUrl: "https://localhost:8080",
}));
di.override(apiKubeInjectable, () => createKubeJsonApi({
serverAddress: `http://127.0.0.1:9999`,
apiBase: "/api-kube",
}));
registerApiSpy = jest.spyOn(di.inject(apiManagerInjectable), "registerApi");
apiManager = di.inject(apiManagerInjectable);
const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
@@ -79,7 +71,7 @@ describe("KubeApi", () => {
it("requests version list from the api group from the initial apiBase", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io",
{
headers: {
"content-type": "application/json",
@@ -92,8 +84,8 @@ describe("KubeApi", () => {
describe("when the version list from the api group resolves", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io", JSON.stringify({
apiVersion: "v1",
kind: "APIGroup",
name: "networking.k8s.io",
@@ -117,7 +109,7 @@ describe("KubeApi", () => {
it("requests resources from the versioned api group from the initial apiBase", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1",
{
headers: {
"content-type": "application/json",
@@ -130,8 +122,8 @@ describe("KubeApi", () => {
describe("when resource request fufills with a resource", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1", JSON.stringify({
resources: [{
name: "ingresses",
}],
@@ -141,7 +133,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo",
{
headers: {
"content-type": "application/json",
@@ -159,8 +151,8 @@ describe("KubeApi", () => {
}));
});
it("registers the api with the changes info", () => {
expect(registerApiSpy).toBeCalledWith(ingressApi);
it("api is retrievable with the new apiBase", () => {
expect(apiManager.getApi("/apis/networking.k8s.io/v1/ingresses")).toBeDefined();
});
describe("when the request resolves with no data", () => {
@@ -168,8 +160,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({})),
);
result = await getCall;
});
@@ -193,7 +185,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1",
{
headers: {
"content-type": "application/json",
@@ -208,8 +200,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -226,8 +218,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo", JSON.stringify({
apiVersion: "v1",
kind: "Ingress",
metadata: {
@@ -260,7 +252,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1",
{
headers: {
"content-type": "application/json",
@@ -275,8 +267,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -292,8 +284,8 @@ describe("KubeApi", () => {
describe("when resource request fufills with no resource", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1", JSON.stringify({
resources: [],
})),
);
@@ -301,7 +293,7 @@ describe("KubeApi", () => {
it("requests resources from the second versioned api group from the initial apiBase", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1",
{
headers: {
"content-type": "application/json",
@@ -316,8 +308,8 @@ describe("KubeApi", () => {
describe("when resource request fufills with a resource", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1", JSON.stringify({
resources: [{
name: "ingresses",
}],
@@ -327,7 +319,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo",
{
headers: {
"content-type": "application/json",
@@ -345,8 +337,12 @@ describe("KubeApi", () => {
}));
});
it("registers the api with the changes info", () => {
expect(registerApiSpy).toBeCalledWith(ingressApi);
it("api is retrievable with the new apiBase", () => {
expect(apiManager.getApi("/apis/networking.k8s.io/v1beta1/ingresses")).toBeDefined();
});
it("api is retrievable with the old apiBase", () => {
expect(apiManager.getApi("/apis/networking.k8s.io/v1/ingresses")).toBeDefined();
});
describe("when the request resolves with no data", () => {
@@ -354,8 +350,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})),
);
result = await getCall;
});
@@ -379,7 +375,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1",
{
headers: {
"content-type": "application/json",
@@ -394,8 +390,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -412,8 +408,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({
apiVersion: "v1",
kind: "Ingress",
metadata: {
@@ -446,7 +442,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1",
"https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1",
{
headers: {
"content-type": "application/json",
@@ -461,8 +457,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -480,8 +476,8 @@ describe("KubeApi", () => {
describe("when the version list from the api group resolves with no versions", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/networking.k8s.io"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/networking.k8s.io", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/networking.k8s.io"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/networking.k8s.io", JSON.stringify({
"metadata": {},
"status": "Failure",
"message": "the server could not find the requested resource",
@@ -501,7 +497,7 @@ describe("KubeApi", () => {
it("requests the resources from the base api url from the fallback api", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/extensions",
"https://127.0.0.1:12345/api-kube/apis/extensions",
{
headers: {
"content-type": "application/json",
@@ -514,8 +510,8 @@ describe("KubeApi", () => {
describe("when resource request fufills with a resource", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/extensions"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions", JSON.stringify({
apiVersion: "v1",
kind: "APIGroup",
name: "extensions",
@@ -535,7 +531,7 @@ describe("KubeApi", () => {
it("requests resource versions from the versioned api group from the fallback apiBase", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1",
"https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1",
{
headers: {
"content-type": "application/json",
@@ -548,8 +544,8 @@ describe("KubeApi", () => {
describe("when the preferred version request resolves to v1beta1", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions", JSON.stringify({
resources: [{
name: "ingresses",
}],
@@ -559,7 +555,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo",
"https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo",
{
headers: {
"content-type": "application/json",
@@ -577,8 +573,8 @@ describe("KubeApi", () => {
}));
});
it("registers the api with the changes info", () => {
expect(registerApiSpy).toBeCalledWith(ingressApi);
it("api is retrievable with the new apiBase", () => {
expect(apiManager.getApi("/apis/extensions/v1beta1/ingresses")).toBeDefined();
});
describe("when the request resolves with no data", () => {
@@ -586,8 +582,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({})),
);
result = await getCall;
});
@@ -611,7 +607,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1",
"https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1",
{
headers: {
"content-type": "application/json",
@@ -626,8 +622,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -644,8 +640,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({
["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo", JSON.stringify({
apiVersion: "v1beta1",
kind: "Ingress",
metadata: {
@@ -678,7 +674,7 @@ describe("KubeApi", () => {
it("makes the request to get the resource", () => {
expect(fetchMock.mock.lastCall).toMatchObject([
"http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1",
"https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1",
{
headers: {
"content-type": "application/json",
@@ -693,8 +689,8 @@ describe("KubeApi", () => {
beforeEach(async () => {
await fetchMock.resolveSpecific(
["http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
["https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1"],
createMockResponseFromString("https://127.0.0.1:12345/api-kube/apis/extensions/v1beta1/namespaces/default/ingresses/foo1", JSON.stringify({})),
);
result = await getCall;
});
@@ -5,11 +5,12 @@
import type { KubeObjectStore } from "../kube-object.store";
import { action, observable, makeObservable } from "mobx";
import { autoBind, isDefined, iter } from "../../utils";
import type { IComputedValue } from "mobx";
import { autorun, action, observable } from "mobx";
import type { KubeApi } from "../kube-api";
import type { KubeJsonApiDataFor, KubeObject, ObjectReference } from "../kube-object";
import type { KubeObject, ObjectReference } from "../kube-object";
import { parseKubeApi, createKubeApiURL } from "../kube-api-parse";
import { chain, find } from "../../utils/iter";
export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any>
? Store
@@ -21,25 +22,53 @@ export type KubeObjectStoreFrom<Api> = Api extends KubeApi<infer KubeObj, infer
? KubeObjectStore<KubeObj, Api, ApiData>
: never;
export class ApiManager {
private readonly apis = observable.map<string, KubeApi>();
private readonly stores = observable.map<string, KubeObjectStore>();
export type FindApiCallback = (api: KubeApi<KubeObject>) => boolean;
constructor() {
makeObservable(this);
autoBind(this);
interface Dependencies {
readonly apis: IComputedValue<KubeApi[]>;
readonly stores: IComputedValue<KubeObjectStore[]>;
}
export class ApiManager {
private readonly externalApis = observable.array<KubeApi>();
private readonly externalStores = observable.array<KubeObjectStore>();
private readonly apis = observable.map<string, KubeApi>();
constructor(private readonly dependencies: Dependencies) {
// NOTE: this is done to preserve the old behaviour of an API being discoverable using all previous apiBases
autorun(() => {
const apis = chain(this.dependencies.apis.get().values())
.concat(this.externalApis.values());
const removedApis = new Set(this.apis.values());
for (const api of apis) {
removedApis.delete(api);
this.apis.set(api.apiBase, api);
}
for (const api of removedApis) {
for (const [apiBase, storedApi] of this.apis) {
if (storedApi === api) {
this.apis.delete(apiBase);
}
}
}
});
}
getApi(pathOrCallback: string | ((api: KubeApi<KubeObject>) => boolean)) {
if (typeof pathOrCallback === "string") {
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase);
getApi(pathOrCallback: string | FindApiCallback) {
if (typeof pathOrCallback === "function") {
return find(this.apis.values(), pathOrCallback);
}
return iter.find(this.apis.values(), pathOrCallback ?? (() => true));
const { apiBase } = parseKubeApi(pathOrCallback);
return this.apis.get(apiBase);
}
getApiByKind(kind: string, apiVersion: string) {
return iter.find(this.apis.values(), api => api.kind === kind && api.apiVersionWithGroup === apiVersion);
return this.getApi(api => api.kind === kind && api.apiVersionWithGroup === apiVersion);
}
registerApi<Api>(api: RegisterableApi<Api>): void;
@@ -47,45 +76,23 @@ export class ApiManager {
* @deprecated Just register the `api` by itself
*/
registerApi<Api>(apiBase: string, api: RegisterableApi<Api>): void;
registerApi<Api>(apiBaseRaw: string | RegisterableApi<Api>, apiRaw?: RegisterableApi<Api>) {
const api = typeof apiBaseRaw === "string"
? apiRaw
: apiBaseRaw;
if (!api?.apiBase) {
return;
}
if (!this.apis.has(api.apiBase)) {
this.stores.forEach((store) => {
if (store.api === api) {
this.stores.set(api.apiBase, store);
}
});
this.apis.set(api.apiBase, api);
registerApi<Api>(...args: [RegisterableApi<Api>] | [string, RegisterableApi<Api>]) {
if (args.length === 1) {
this.externalApis.push(args[0]);
} else {
this.externalApis.push(args[1]);
}
}
protected resolveApi(api: undefined | string | KubeApi): KubeApi | undefined {
if (!api) {
return undefined;
}
unregisterApi(apiOrBase: string | KubeApi<KubeObject>) {
if (typeof apiOrBase === "string") {
const api = this.externalApis.find(api => api.apiBase === apiOrBase);
if (typeof api === "string") {
return this.getApi(api);
}
return api;
}
unregisterApi(api: string | KubeApi<KubeObject>) {
if (typeof api === "string") this.apis.delete(api);
else {
const apis = Array.from(this.apis.entries());
const entry = apis.find(entry => entry[1] === api);
if (entry) this.unregisterApi(entry[0]);
if (api) {
this.externalApis.remove(api);
}
} else {
this.unregisterApi(apiOrBase.apiBase);
}
}
@@ -93,15 +100,11 @@ export class ApiManager {
/**
* @deprecated KubeObjectStore's should only every be about a single KubeApi type
*/
registerStore<KubeObj extends KubeObject>(store: KubeObjectStore<KubeObj, KubeApi<KubeObj>, KubeJsonApiDataFor<KubeObj>>, apis: KubeApi<KubeObj>[]): void;
registerStore<KubeObj>(store: RegisterableStore<KubeObj>, apis: KubeApi<KubeObject>[]): void;
@action
registerStore<KubeObj extends KubeObject>(store: KubeObjectStore<KubeObj, KubeApi<KubeObj>, KubeJsonApiDataFor<KubeObj>>, apis: KubeApi<KubeObj>[] = [store.api]): void {
for (const api of apis.filter(isDefined)) {
if (api.apiBase) {
this.stores.set(api.apiBase, store as never);
}
}
registerStore<KubeObj>(store: RegisterableStore<KubeObj>): void {
this.externalStores.push(store);
}
getStore(api: string | undefined): KubeObjectStore | undefined;
@@ -110,14 +113,23 @@ export class ApiManager {
* @deprecated use an actual cast instead of hiding it with this unused type param
*/
getStore<Store extends KubeObjectStore>(api: string | KubeApi): Store | undefined ;
getStore(api: string | KubeApi | undefined): KubeObjectStore | undefined {
const { apiBase } = this.resolveApi(api) ?? {};
if (apiBase) {
return this.stores.get(apiBase);
getStore(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined {
if (!apiOrBase) {
return undefined;
}
return undefined;
const { apiBase } = typeof apiOrBase === "string"
? parseKubeApi(apiOrBase)
: apiOrBase;
const api = this.getApi(apiBase);
if (!api) {
return undefined;
}
return chain(this.dependencies.stores.get().values())
.concat(this.externalStores.values())
.find(store => store.api.apiBase === api.apiBase);
}
lookupApiLink(ref: ObjectReference, parentObject?: KubeObject): string {
@@ -132,7 +144,7 @@ export class ApiManager {
const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
if (api) {
return api.getUrl({ namespace, name });
return api.formatUrlForNotListing({ namespace, name });
}
// lookup api by generated resource link
@@ -151,7 +163,7 @@ export class ApiManager {
const apiByKind = this.getApi(api => api.kind === kind);
if (apiByKind) {
return apiByKind.getUrl({ name, namespace });
return apiByKind.formatUrlForNotListing({ name, namespace });
}
// otherwise generate link with default prefix
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { KubeObjectStore } from "../kube-object.store";
export const kubeObjectStoreInjectionToken = getInjectionToken<KubeObjectStore<any, any, any>>({
id: "kube-object-store-token",
});
@@ -2,29 +2,28 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import type { KubeObjectStore } from "../kube-object.store";
import { ApiManager } from "./api-manager";
export const kubeObjectStoreInjectionToken = getInjectionToken<KubeObjectStore<any, any, any>>({
id: "kube-object-store-token",
});
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import { kubeObjectStoreInjectionToken } from "./kube-object-store-token";
import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
import { computed } from "mobx";
const apiManagerInjectable = getInjectable({
id: "api-manager",
instantiate: (di) => {
const apiManager = new ApiManager();
const computedInjectMany = di.inject(computedInjectManyInjectable);
const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken);
if (di.inject(storesAndApisCanBeCreatedInjectionToken)) {
const stores = di.injectMany(kubeObjectStoreInjectionToken);
for (const store of stores) {
apiManager.registerStore(store);
}
}
return apiManager;
return new ApiManager({
apis: storesAndApisCanBeCreated
? computedInjectMany(kubeApiInjectionToken)
: computed(() => []),
stores: storesAndApisCanBeCreated
? computedInjectMany(kubeObjectStoreInjectionToken)
: computed(() => []),
});
},
});
@@ -72,6 +72,27 @@ export type HorizontalPodAutoscalerMetricSpec =
| OptionVarient<HpaMetricType.Pods, BaseHorizontalPodAutoscalerMetricSpec, "pods">
| OptionVarient<HpaMetricType.ContainerResource, BaseHorizontalPodAutoscalerMetricSpec, "containerResource">;
interface HorizontalPodAutoscalerBehavior {
scaleUp?: HPAScalingRules;
scaleDown?: HPAScalingRules;
}
interface HPAScalingRules {
stabilizationWindowSecond?: number;
selectPolicy?: ScalingPolicySelect;
policies?: HPAScalingPolicy[];
}
type ScalingPolicySelect = string;
interface HPAScalingPolicy {
type: HPAScalingPolicyType;
value: number;
periodSeconds: number;
}
type HPAScalingPolicyType = string;
export interface ContainerResourceMetricStatus {
container: string;
currentAverageUtilization?: number;
@@ -132,6 +153,7 @@ export interface HorizontalPodAutoscalerSpec {
minReplicas?: number;
maxReplicas: number;
metrics?: HorizontalPodAutoscalerMetricSpec[];
behavior?: HorizontalPodAutoscalerBehavior;
}
export interface HorizontalPodAutoscalerStatus {
@@ -153,7 +175,7 @@ export class HorizontalPodAutoscaler extends KubeObject<
> {
static readonly kind = "HorizontalPodAutoscaler";
static readonly namespaced = true;
static readonly apiBase = "/apis/autoscaling/v2beta1/horizontalpodautoscalers";
static readonly apiBase = "/apis/autoscaling/v2/horizontalpodautoscalers";
getMaxPods() {
return this.spec.maxReplicas ?? 0;
@@ -204,8 +226,15 @@ export class HorizontalPodAutoscaler extends KubeObject<
export class HorizontalPodAutoscalerApi extends KubeApi<HorizontalPodAutoscaler> {
constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) {
super(deps, {
objectConstructor: HorizontalPodAutoscaler,
...opts ?? {},
objectConstructor: HorizontalPodAutoscaler,
checkPreferredVersion: true,
// Kubernetes < 1.26
fallbackApiBases: [
"/apis/autoscaling/v2beta2/horizontalpodautoscalers",
"/apis/autoscaling/v2beta1/horizontalpodautoscalers",
"/apis/autoscaling/v1/horizontalpodautoscalers",
],
});
}
}
+5 -29
View File
@@ -20,10 +20,9 @@ import type { Patch } from "rfc6902";
import assert from "assert";
import type { PartialDeep } from "type-fest";
import type { Logger } from "../logger";
import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import autoRegistrationEmitterInjectable from "./api-manager/auto-registration-emitter.injectable";
import type AbortController from "abort-controller";
import { matches } from "lodash/fp";
import { makeObservable, observable } from "mobx";
/**
* The options used for creating a `KubeApi`
@@ -206,30 +205,6 @@ export interface DeleteResourceDescriptor extends ResourceDescriptor {
propagationPolicy?: PropagationPolicy;
}
/**
* @deprecated In the new extension API, don't expose `KubeApi`'s constructor
*/
function legacyRegisterApi(api: KubeApi<any, any>): void {
try {
/**
* This function throws if called in `main`, so the `try..catch` is to make sure that doesn't
* leak.
*
* However, we need this code to be run in `renderer` so that the auto registering of `KubeApi`
* instances still works. That auto registering never worked or was applicable in `main` because
* there is no "single cluster" on `main`.
*
* TODO: rearchitect this design pattern in the new extension API
*/
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.renderer);
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
setImmediate(() => autoRegistrationEmitter.emit("kubeApi", api));
} catch {
// ignore error
}
}
export interface KubeApiDependencies {
readonly logger: Logger;
readonly maybeKubeApi: KubeJsonApi | undefined;
@@ -241,7 +216,9 @@ export class KubeApi<
> {
readonly kind: string;
readonly apiVersion: string;
apiBase: string;
@observable apiBase: string;
apiPrefix: string;
apiGroup: string;
apiVersionPreferred: string | undefined;
@@ -288,7 +265,7 @@ export class KubeApi<
this.apiResource = resource;
this.request = request;
this.objectConstructor = objectConstructor;
legacyRegisterApi(this);
makeObservable(this);
}
get apiVersionWithGroup() {
@@ -347,7 +324,6 @@ export class KubeApi<
this.apiGroup = apiGroup;
this.apiVersionPreferred = apiVersionPreferred;
this.apiBase = this.computeApiBase();
legacyRegisterApi(this);
}
}
-16
View File
@@ -1,16 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import applicationInformationToken from "./vars/application-information-token";
import type { ApplicationInformation } from "./vars/application-information-token";
import { bundledExtensionInjectionToken } from "../extensions/extension-discovery/bundled-extension-token";
import * as extensionApi from "../extensions/common-api";
// @experimental
export {
applicationInformationToken,
ApplicationInformation,
bundledExtensionInjectionToken,
extensionApi,
};
+1 -1
View File
@@ -5,7 +5,7 @@
import applicationInformationToken from "./vars/application-information-token";
import type { ApplicationInformation } from "./vars/application-information-token";
import { bundledExtensionInjectionToken } from "../extensions/extension-discovery/bundled-extension-token";
import * as extensionApi from "../extensions/common-api";
import * as extensionApi from "../extensions/common-api"
// @experimental
export {
+21 -17
View File
@@ -13,17 +13,21 @@ export type KubeResource =
export interface KubeApiResource {
kind: string;
group: string;
group: string; // api-group, if empty then "core"
apiName: string;
namespaced: boolean;
}
export interface KubeApiResourceDescriptor {
apiName: string;
group: string;
group: string; // api-group, if empty then "core"
}
export const formatKubeApiResource = (res: KubeApiResourceDescriptor) => `${res.group}/${res.apiName}`;
export const formatKubeApiResource = (desc: KubeApiResourceDescriptor) => (
desc.group
? `${desc.group}/${desc.apiName}`
: desc.apiName
);
export interface KubeApiResourceData {
kind: string; // resource type (e.g. "Namespace")
@@ -44,7 +48,7 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
},
configmaps: {
kind: "ConfigMap",
group: "v1",
group: "",
namespaced: true,
},
cronjobs: {
@@ -69,12 +73,12 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
},
endpoints: {
kind: "Endpoint",
group: "v1",
group: "",
namespaced: true,
},
events: {
kind: "Event",
group: "v1",
group: "",
namespaced: true,
},
horizontalpodautoscalers: {
@@ -99,17 +103,17 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
},
namespaces: {
kind: "Namespace",
group: "v1",
group: "",
namespaced: false,
},
limitranges: {
kind: "LimitRange",
group: "v1",
group: "",
namespaced: true,
},
leases: {
kind: "Lease",
group: "v1",
group: "",
namespaced: true,
},
networkpolicies: {
@@ -119,22 +123,22 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
},
nodes: {
kind: "Node",
group: "v1",
group: "",
namespaced: false,
},
persistentvolumes: {
kind: "PersistentVolume",
group: "v1",
group: "",
namespaced: false,
},
persistentvolumeclaims: {
kind: "PersistentVolumeClaim",
group: "v1",
group: "",
namespaced: true,
},
pods: {
kind: "Pod",
group: "v1",
group: "",
namespaced: true,
},
poddisruptionbudgets: {
@@ -159,7 +163,7 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
},
resourcequotas: {
kind: "ResourceQuota",
group: "v1",
group: "",
namespaced: true,
},
replicasets: {
@@ -179,17 +183,17 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
},
secrets: {
kind: "Secret",
group: "v1",
group: "",
namespaced: true,
},
serviceaccounts: {
kind: "ServiceAccount",
group: "v1",
group: "",
namespaced: true,
},
services: {
kind: "Service",
group: "v1",
group: "",
namespaced: true,
},
statefulsets: {
@@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { join, nth, reduce } from "../iter";
import { join, nth, reduce, concat } from "../iter";
describe("iter", () => {
describe("reduce", () => {
@@ -39,4 +39,30 @@ describe("iter", () => {
expect(nth(["a", "b"], 0)).toBe("a");
});
});
describe("concat", () => {
it("should yield undefined for empty args", () => {
const iter = concat();
expect(iter.next()).toEqual({ done: true });
});
it("should yield undefined for only empty args", () => {
const iter = concat([].values(), [].values(), [].values(), [].values());
expect(iter.next()).toEqual({ done: true });
});
it("should yield all of the first and then all of the second", () => {
const iter = concat([1, 2, 3].values(), [4, 5, 6].values());
expect(iter.next()).toEqual({ done: false, value: 1 });
expect(iter.next()).toEqual({ done: false, value: 2 });
expect(iter.next()).toEqual({ done: false, value: 3 });
expect(iter.next()).toEqual({ done: false, value: 4 });
expect(iter.next()).toEqual({ done: false, value: 5 });
expect(iter.next()).toEqual({ done: false, value: 6 });
expect(iter.next()).toEqual({ done: true });
});
});
});
+10
View File
@@ -12,6 +12,7 @@ interface Iterator<T> extends Iterable<T> {
collect<U>(fn: (values: Iterable<T>) => U): U;
map<U>(fn: (val: T) => U): Iterator<U>;
flatMap<U>(fn: (val: T) => U[]): Iterator<U>;
concat(src2: IterableIterator<T>): Iterator<T>;
join(sep?: string): string;
}
@@ -24,6 +25,7 @@ export function chain<T>(src: IterableIterator<T>): Iterator<T> {
find: (fn) => find(src, fn),
join: (sep) => join(src, sep),
collect: (fn) => fn(src),
concat: (src2) => chain(concat(src, src2)),
[Symbol.iterator]: () => src,
};
}
@@ -236,3 +238,11 @@ export function every<T>(src: Iterable<T>, fn: (val: T) => any): boolean {
return true;
}
export function* concat<T>(...sources: IterableIterator<T>[]): IterableIterator<T> {
for (const source of sources) {
for (const val of source) {
yield val;
}
}
}
@@ -26,10 +26,12 @@ import loggerInjectable from "../../common/logger.injectable";
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import maybeKubeApiInjectable from "../../common/k8s-api/maybe-kube-api.injectable";
import { DeploymentApi as InternalDeploymentApi, IngressApi as InternalIngressApi, NodeApi, PersistentVolumeClaimApi, PodApi } from "../../common/k8s-api/endpoints";
import { storesAndApisCanBeCreatedInjectionToken } from "../../common/k8s-api/stores-apis-can-be-created.token";
export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable);
export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable);
export const forRemoteCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForRemoteClusterInjectable);
export const createResourceStack = asLegacyGlobalFunctionForExtensionApi(createResourceStackInjectable);
const getKubeApiDeps = (): KubeApiDependencies => {
const di = getLegacyGlobalDiForExtensionApi();
@@ -45,7 +47,16 @@ function KubeApiCstr<
Object extends KubeObject = KubeObject,
Data extends KubeJsonApiDataFor<Object> = KubeJsonApiDataFor<Object>,
>(opts: KubeApiOptions<Object, Data>) {
return new InternalKubeApi(getKubeApiDeps(), opts);
const api = new InternalKubeApi(getKubeApiDeps(), opts);
const di = getLegacyGlobalDiForExtensionApi();
const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken);
if (storesAndApisCanBeCreated) {
apiManager.registerApi(api);
}
return api;
}
export const KubeApi = KubeApiCstr as unknown as new<
@@ -53,8 +64,6 @@ export const KubeApi = KubeApiCstr as unknown as new<
Data extends KubeJsonApiDataFor<Object> = KubeJsonApiDataFor<Object>,
>(opts: KubeApiOptions<Object, Data>) => InternalKubeApi<Object, Data>;
export const createResourceStack = asLegacyGlobalFunctionForExtensionApi(createResourceStackInjectable);
/**
* @deprecated Switch to using `Common.createResourceStack` instead
*/
@@ -174,31 +183,31 @@ export interface IgnoredKubeApiOptions {
// NOTE: these *Constructor functions MUST be `function` to work with `new X()`
function PodsApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) {
return new PodApi(getKubeApiDeps(), opts ?? {});
return new PodApi(getKubeApiDeps(), opts);
}
export const PodsApi = PodsApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => PodApi;
function NodesApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) {
return new NodeApi(getKubeApiDeps(), opts ?? {});
return new NodeApi(getKubeApiDeps(), opts);
}
export const NodesApi = NodesApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => NodeApi;
function DeploymentApiConstructor(opts?: DerivedKubeApiOptions) {
return new InternalDeploymentApi(getKubeApiDeps(), opts ?? {});
return new InternalDeploymentApi(getKubeApiDeps(), opts);
}
export const DeploymentApi = DeploymentApiConstructor as unknown as new (opts?: DerivedKubeApiOptions) => InternalDeploymentApi;
function IngressApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) {
return new InternalIngressApi(getKubeApiDeps(), opts ?? {});
return new InternalIngressApi(getKubeApiDeps(), opts);
}
export const IngressApi = IngressApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => InternalIngressApi;
function PersistentVolumeClaimsApiConstructor(opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) {
return new PersistentVolumeClaimApi(getKubeApiDeps(), opts ?? {});
return new PersistentVolumeClaimApi(getKubeApiDeps(), opts);
}
export const PersistentVolumeClaimsApi = PersistentVolumeClaimsApiConstructor as unknown as new (opts?: DerivedKubeApiOptions & IgnoredKubeApiOptions) => PersistentVolumeClaimApi;
@@ -14,3 +14,4 @@ export type { CustomCategoryViewProps, CustomCategoryViewComponents, CustomCateg
export type { ShellEnvModifier, ShellEnvContext } from "../../main/shell-session/shell-env-modifier/shell-env-modifier-registration";
export type { KubeObjectContextMenuItem, KubeObjectOnContextMenuOpenContext, KubeObjectOnContextMenuOpen, KubeObjectHandlers, KubeObjectHandlerRegistration } from "../../renderer/kube-object/handler";
export type { TrayMenuRegistration } from "../../main/tray/tray-menu-registration";
export type { MenuRegistration } from "../../features/application-menu/main/menu-registration";
@@ -12,7 +12,7 @@ import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/
import type { LensMainExtensionDependencies } from "./lens-extension-set-dependencies";
export class LensMainExtension extends LensExtension<LensMainExtensionDependencies> {
appMenus: MenuRegistration[] = [];
appMenus: MenuRegistration[] | IComputedValue<MenuRegistration[]> = [];
trayMenus: TrayMenuRegistration[] | IComputedValue<TrayMenuRegistration[]> = [];
/**
@@ -2,6 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { computed } from "mobx";
import type { Injectable } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
@@ -25,11 +26,15 @@ const applicationMenuItemRegistratorInjectable = getInjectable({
const toRecursedInjectables = toRecursedInjectablesFor(logError);
return (ext: LensExtension) => {
const extension = ext as LensMainExtension;
const mainExtension = ext as LensMainExtension;
return extension.appMenus.flatMap(
toRecursedInjectables([extension.sanitizedExtensionId]),
);
return computed(() => {
const appMenus = Array.isArray(mainExtension.appMenus) ? mainExtension.appMenus : mainExtension.appMenus.get();
return appMenus.flatMap(
toRecursedInjectables([mainExtension.sanitizedExtensionId]),
);
});
};
},
@@ -3,7 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { MenuItemConstructorOptions } from "electron";
import type { IComputedValue } from "mobx";
export interface MenuRegistration extends MenuItemConstructorOptions {
export type MenuRegistration = {
parentId: string;
}
visible?: IComputedValue<boolean> | boolean;
} & Omit<MenuItemConstructorOptions, "visible">;
@@ -13,6 +13,7 @@ import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager
import type { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
import type { KubeApi } from "../../../../common/k8s-api/kube-api";
import showDetailsInjectable from "../../../../renderer/components/kube-detail-params/show-details.injectable";
import assert from "assert";
describe("reactively hide kube object detail item", () => {
let builder: ApplicationBuilder;
@@ -89,6 +90,10 @@ describe("reactively hide kube object detail item", () => {
runInAction(() => {
someObservable.set(true);
});
const apiManager = builder.applicationWindow.only.di.inject(apiManagerInjectable);
assert(apiManager.getStore("/apis/some-api-version/some-kind"));
});
it("renders", () => {
@@ -66,7 +66,7 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
builder.allowKubeResource({
apiName: "namespaces",
group: "v1",
group: "",
});
});
@@ -37,7 +37,7 @@ describe("cluster/namespaces - edit namespaces from previously opened tab", () =
builder.allowKubeResource({
apiName: "namespaces",
group: "v1",
group: "",
});
});
@@ -52,7 +52,7 @@ describe("cluster - visibility of sidebar items", () => {
beforeEach(() => {
builder.allowKubeResource({
apiName: "namespaces",
group: "v1",
group: "",
});
});
@@ -77,7 +77,7 @@ const testRouteInjectable = getInjectable({
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "namespaces",
group: "v1",
group: "",
}),
}),
@@ -15,7 +15,7 @@ describe("workload overview", () => {
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
applicationBuilder.allowKubeResource({
apiName: "pods",
group: "v1",
group: "",
});
rendered = await applicationBuilder.render();
});
+5 -5
View File
@@ -145,31 +145,31 @@ export class ClusterManager {
} else {
entity.status.phase = (() => {
if (!cluster) {
this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="no cluster"`);
this.dependencies.logger.silly(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="no cluster"`);
return LensKubernetesClusterStatus.DISCONNECTED;
}
if (cluster.accessible) {
this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTED, reason="cluster is accessible"`);
this.dependencies.logger.silly(`${logPrefix} setting entity ${entity.getName()} to CONNECTED, reason="cluster is accessible"`);
return LensKubernetesClusterStatus.CONNECTED;
}
if (!cluster.disconnected) {
this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to CONNECTING, reason="cluster is not disconnected"`);
this.dependencies.logger.silly(`${logPrefix} setting entity ${entity.getName()} to CONNECTING, reason="cluster is not disconnected"`);
return LensKubernetesClusterStatus.CONNECTING;
}
// Extensions are not allowed to use the Lens specific status phases
if (!lensSpecificClusterStatuses.has(entity?.status?.phase)) {
this.dependencies.logger.debug(`${logPrefix} not clearing entity ${entity.getName()} status, reason="custom string"`);
this.dependencies.logger.silly(`${logPrefix} not clearing entity ${entity.getName()} status, reason="custom string"`);
return entity.status.phase;
}
this.dependencies.logger.debug(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="fallthrough"`);
this.dependencies.logger.silly(`${logPrefix} setting entity ${entity.getName()} to DISCONNECTED, reason="fallthrough"`);
return LensKubernetesClusterStatus.DISCONNECTED;
})();
@@ -16,7 +16,7 @@ const requestCoreApiVersionsInjectable = getInjectable({
const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
return versions.map(version => ({
group: version,
group: "",
path: `/api/${version}`,
}));
};
@@ -7,7 +7,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { ClusterOverviewStorageState } from "./cluster-overview-store";
import { ClusterOverviewStore, MetricNodeRole, MetricType } from "./cluster-overview-store";
import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
import clusterApiInjectable from "../../../../common/k8s-api/endpoints/cluster.api.injectable";
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import assert from "assert";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import horizontalPodAutoscalerApiInjectable from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import leaseApiInjectable from "../../../common/k8s-api/endpoints/lease.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import limitRangeApiInjectable from "../../../common/k8s-api/endpoints/limit-range.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import configMapApiInjectable from "../../../common/k8s-api/endpoints/config-map.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import podDisruptionBudgetApiInjectable from "../../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import resourceQuotaApiInjectable from "../../../common/k8s-api/endpoints/resource-quota.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import getPodByIdInjectable from "../+workloads-pods/get-pod-by-id.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import kubeEventApiInjectable from "../../../common/k8s-api/endpoints/events.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { NamespaceStore } from "./store";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import createStorageInjectable from "../../utils/create-storage/create-storage.injectable";
import namespaceApiInjectable from "../../../common/k8s-api/endpoints/namespace.api.injectable";
import assert from "assert";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import endpointsApiInjectable from "../../../common/k8s-api/endpoints/endpoint.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,10 +4,9 @@
*/
import assert from "assert";
import { getInjectable } from "@ogre-tools/injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import ingressClassApiInjectable from "../../../common/k8s-api/endpoints/ingress-class.api.injectable";
import { IngressClassStore } from "./ingress-class-store";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import ingressApiInjectable from "../../../common/k8s-api/endpoints/ingress.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import networkPolicyApiInjectable from "../../../common/k8s-api/endpoints/network-policy.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import podSecurityPolicyApiInjectable from "../../../common/k8s-api/endpoints/pod-security-policy.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import getPersistentVolumesByStorageClassInjectable from "../+storage-volumes/get-persisten-volumes-by-storage-class.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import persistentVolumeApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
@@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../../../../common/k8s-api/stores-apis-can-be-created.token";
import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role-binding.api.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
import { ClusterRoleBindingStore } from "./store";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
@@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../../../../common/k8s-api/stores-apis-can-be-created.token";
import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role.api.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
import { ClusterRoleStore } from "./store";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
import roleBindingApiInjectable from "../../../../common/k8s-api/endpoints/role-binding.api.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import roleApiInjectable from "../../../../common/k8s-api/endpoints/role.api.injectable";
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
import { RoleStore } from "./store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
@@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import serviceAccountApiInjectable from "../../../../common/k8s-api/endpoints/service-account.api.injectable";
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/kube-object-store-token";
import { ServiceAccountStore } from "./store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import getJobsByOwnerInjectable from "../+workloads-jobs/get-jobs-by-owner.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import cronJobApiInjectable from "../../../common/k8s-api/endpoints/cron-job.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -7,7 +7,7 @@ import assert from "assert";
import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable";
import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set.api.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import { DaemonSetStore } from "./store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import { storesAndApisCanBeCreatedInjectionToken } from "../../../common/k8s-api/stores-apis-can-be-created.token";
import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable";
import { DeploymentStore } from "./store";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import jobApiInjectable from "../../../common/k8s-api/endpoints/job.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -21,7 +21,7 @@ const podsWorkloadInjectable = getInjectable({
return {
resource: {
apiName: "pods",
group: "v1",
group: "",
},
open: navigate,
@@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import podApiInjectable from "../../../common/k8s-api/endpoints/pod.api.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import { PodStore } from "./store";
import podMetricsApiInjectable from "../../../common/k8s-api/endpoints/pod-metrics.api.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -7,7 +7,7 @@ import assert from "assert";
import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable";
import replicaSetApiInjectable from "../../../common/k8s-api/endpoints/replica-set.api.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import { ReplicaSetStore } from "./store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import statefulSetApiInjectable from "../../../common/k8s-api/endpoints/stateful-set.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -0,0 +1,121 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kube-object-list-layout given pod store renders 1`] = `
<body>
<div>
<div>
<div
class="ItemListLayout flex column KubeObjectListLayout Pods"
>
<div
class="header flex gaps align-center"
>
<h5
class="title"
>
Pods
</h5>
<div
class="info-panel box grow"
>
0 items
</div>
<div
class="NamespaceSelectFilterParent"
data-testid="namespace-select-filter"
>
<div
class="Select theme-dark NamespaceSelect NamespaceSelectFilter css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-kube-object-list-layout-namespace-select-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-13cymwt-control"
>
<div
class="Select__value-container Select__value-container--is-multi css-1fdsijx-ValueContainer"
>
<div
class="Select__placeholder css-1jqq78o-placeholder"
id="react-select-kube-object-list-layout-namespace-select-input-placeholder"
>
All namespaces
</div>
<div
class="Select__input-container css-qbdosj-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-kube-object-list-layout-namespace-select-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="kube-object-list-layout-namespace-select-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1u9des2-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-1xc3v61-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="items box grow flex column"
>
<div
class="Table flex column KubeObjectListLayout Pods box grow dark selectable scrollable autoSize virtual"
>
<div
class="Spinner singleColor center"
/>
</div>
<div
class="AddRemoveButtons flex gaps"
/>
</div>
</div>
</div>
</div>
</body>
`;
@@ -0,0 +1,108 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import "@testing-library/jest-dom/extend-expect";
import type { RenderResult } from "@testing-library/react";
import React from "react";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor";
import { KubeObjectListLayout } from "./index";
import appPathsStateInjectable from "../../../common/app-paths/app-paths-state.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
import type { PodStore } from "../+workloads-pods/store";
describe("kube-object-list-layout", () => {
let di: DiContainer;
let render: DiRender;
let podStore: PodStore;
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
const createCluster = di.inject(createClusterInjectable);
di.override(hostedClusterInjectable, () => createCluster({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",
}, {
clusterServerUrl: "https://localhost:8080",
}));
render = renderFor(di);
di.override(subscribeStoresInjectable, () => jest.fn().mockImplementation(() => jest.fn()));
di.override(kubeSelectedUrlParamInjectable, () => ({
get: () => "path",
}));
di.override(toggleKubeDetailsPaneInjectable, () => null);
di.override(appPathsStateInjectable, () => ({
get: () => ({}),
}));
podStore = di.inject(podStoreInjectable);
});
describe("given pod store", () => {
let result: RenderResult;
it("renders", () => {
result = render((
<div>
<KubeObjectListLayout
className="Pods"
store={podStore}
tableId = "workloads_pods"
isConfigurable
renderHeaderTitle="Pods"
renderTableContents={pod => [
<div key={pod.getName()}>{pod.getName()}</div>,
]}
/>
</div>
));
expect(result.baseElement).toMatchSnapshot();
});
describe("given resourcename", () => {
it("uses resourcename in search placeholder", () => {
result = render((
<div>
<KubeObjectListLayout
className="Pods"
store={podStore}
tableId = "workloads_pods"
isConfigurable
renderHeaderTitle="Pods"
renderTableContents={pod => [
<div key={pod.getName()}>{pod.getName()}</div>,
]}
resourceName="My Custom Items"
searchFilters={[() => null]}
/>
</div>
));
expect(result.getByPlaceholderText("Search My Custom Items...")).toBeInTheDocument();
});
});
});
});
@@ -40,6 +40,12 @@ export interface KubeObjectListLayoutProps<
store: KubeObjectStore<K, A, D>;
dependentStores?: SubscribableStore[];
subscribeStores?: boolean;
/**
* Customize resource name for e.g. search input ("Search <ResourceName>..."")
* If not provided, ResourceNames is used instead with a fallback to resource kind.
*/
resourceName?: string;
}
interface Dependencies {
@@ -132,7 +138,7 @@ class NonInjectedKubeObjectListLayout<
onDetails,
...layoutProps
} = this.props;
const placeholderString = ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind;
const resourceName = this.props.resourceName || ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind;
return (
<ItemListLayout<K, false>
@@ -151,7 +157,7 @@ class NonInjectedKubeObjectListLayout<
),
searchProps: {
...searchProps,
placeholder: `Search ${placeholderString}...`,
placeholder: `Search ${resourceName}...`,
},
info: (
<>
@@ -69,7 +69,7 @@ describe("<ClusterFrame />", () => {
describe("given cluster with list nodes and namespaces permissions", () => {
beforeEach(() => {
// TODO: replace with not using private info
(cluster as any).allowedResources.replace(["v1/nodes", "v1/namespaces"]);
(cluster as unknown as { readonly allowedResources: Cluster["allowedResources"] }).allowedResources.replace(["nodes", "namespaces"]);
});
it("renders", () => {
@@ -110,7 +110,7 @@ describe("<ClusterFrame />", () => {
describe("given cluster without list nodes, but with namespaces permissions", () => {
beforeEach(() => {
(cluster as any).allowedResources.replace(["v1/namespaces"]);
(cluster as unknown as { readonly allowedResources: Cluster["allowedResources"] }).allowedResources.replace(["namespaces"]);
});
it("renders", () => {
@@ -34,7 +34,7 @@ export const WorkloadEvents = withInjectables<Dependencies, WorkloadEventsProps>
getProps: (di, props) => ({
workloadEventsAreAllowed: di.inject(shouldShowResourceInjectionToken, {
apiName: "events",
group: "v1",
group: "",
}),
...props,
}),