diff --git a/package.json b/package.json
index 830939a7cb..d68bce842b 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
"devDependencies": {
"adr": "^1.4.3",
"cross-env": "^7.0.3",
- "lerna": "^6.5.0",
+ "lerna": "^6.5.1",
"rimraf": "^4.1.2"
}
}
diff --git a/packages/core/package.json b/packages/core/package.json
index 94ef65127c..e88c3642fc 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -158,12 +158,12 @@
"hpagent": "^1.2.0",
"http-proxy": "^1.18.1",
"immer": "^9.0.19",
- "joi": "^17.7.0",
+ "joi": "^17.7.1",
"js-yaml": "^4.1.0",
"lodash": "^4.17.15",
"marked": "^4.2.12",
"md5-file": "^5.0.0",
- "mobx": "^6.7.0",
+ "mobx": "^6.8.0",
"mobx-observable-history": "^2.0.3",
"mobx-react": "^7.6.0",
"mobx-utils": "^6.0.4",
@@ -269,7 +269,7 @@
"electron": "^19.1.9",
"electron-builder": "^23.6.0",
"electron-notarize": "^0.3.0",
- "esbuild": "^0.17.7",
+ "esbuild": "^0.17.8",
"esbuild-loader": "^2.21.0",
"eslint": "^8.33.0",
"eslint-import-resolver-typescript": "^3.5.3",
@@ -310,7 +310,7 @@
"react-table": "^7.8.0",
"react-window": "^1.8.8",
"rimraf": "^4.1.2",
- "sass": "^1.58.0",
+ "sass": "^1.58.1",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.2.4",
diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/navigate-to-replication-controllers.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/navigate-to-replication-controllers.injectable.ts
new file mode 100644
index 0000000000..240ad0f37e
--- /dev/null
+++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/navigate-to-replication-controllers.injectable.ts
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import replicationControllersRouteInjectable from "./replicationcontrollers-route.injectable";
+import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token";
+
+const navigateToReplicationControllersInjectable = getInjectable({
+ id: "navigate-to-replicationcontrollers",
+
+ instantiate: (di) => {
+ const navigateToRoute = di.inject(navigateToRouteInjectionToken);
+ const route = di.inject(replicationControllersRouteInjectable);
+
+ return () => navigateToRoute(route);
+ },
+});
+
+export default navigateToReplicationControllersInjectable;
diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts
new file mode 100644
index 0000000000..77d87abc96
--- /dev/null
+++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
+import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
+
+const replicationControllersRouteInjectable = getInjectable({
+ id: "replicationcontrollers-route",
+
+ instantiate: (di) => ({
+ path: "/replicationcontrollers",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "replicationcontrollers",
+ group: "", // core
+ }),
+ }),
+
+ injectionToken: frontEndRouteInjectionToken,
+});
+
+export default replicationControllersRouteInjectable;
diff --git a/packages/core/src/common/k8s-api/endpoints/index.ts b/packages/core/src/common/k8s-api/endpoints/index.ts
index 0314c6b282..8fb6cbdabd 100644
--- a/packages/core/src/common/k8s-api/endpoints/index.ts
+++ b/packages/core/src/common/k8s-api/endpoints/index.ts
@@ -33,6 +33,7 @@ export * from "./pod-metrics.api";
export * from "./pod-security-policy.api";
export * from "./priority-class.api";
export * from "./replica-set.api";
+export * from "./replication-controller.api";
export * from "./resource-quota.api";
export * from "./role.api";
export * from "./role-binding.api";
diff --git a/packages/core/src/common/k8s-api/endpoints/replication-controller.api.injectable.ts b/packages/core/src/common/k8s-api/endpoints/replication-controller.api.injectable.ts
new file mode 100644
index 0000000000..6d65f446b5
--- /dev/null
+++ b/packages/core/src/common/k8s-api/endpoints/replication-controller.api.injectable.ts
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
+import loggerInjectable from "../../logger.injectable";
+import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
+import { ReplicationControllerApi } from "./replication-controller.api";
+
+const replicationControllerApiInjectable = getInjectable({
+ id: "replication-controller-api",
+ instantiate: (di) => {
+ return new ReplicationControllerApi({
+ logger: di.inject(loggerInjectable),
+ maybeKubeApi: di.inject(maybeKubeApiInjectable),
+ });
+ },
+
+ injectionToken: kubeApiInjectionToken,
+});
+
+export default replicationControllerApiInjectable;
diff --git a/packages/core/src/common/k8s-api/endpoints/replication-controller.api.ts b/packages/core/src/common/k8s-api/endpoints/replication-controller.api.ts
new file mode 100644
index 0000000000..b87b7a81a8
--- /dev/null
+++ b/packages/core/src/common/k8s-api/endpoints/replication-controller.api.ts
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api";
+import { KubeApi } from "../kube-api";
+import type {
+ BaseKubeObjectCondition, KubeObjectMetadata,
+ KubeObjectStatus,
+ NamespaceScopedMetadata,
+} from "../kube-object";
+import { KubeObject } from "../kube-object";
+import type { PodTemplateSpec } from "./types";
+
+export class ReplicationControllerApi extends KubeApi {
+ constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) {
+ super(deps, {
+ ...opts ?? {},
+ objectConstructor: ReplicationController,
+ });
+ }
+
+ protected getScaleApiUrl(params: { namespace: string; name: string }) {
+ return `${this.formatUrlForNotListing(params)}/scale`;
+ }
+
+ getScale(params: { namespace: string; name: string }): Promise {
+ return this.request.get(this.getScaleApiUrl(params));
+ }
+
+ scale(params: { namespace: string; name: string }, replicas: number): Promise {
+ return this.request.patch(this.getScaleApiUrl(params), {
+ data: {
+ metadata: params,
+ spec: {
+ replicas,
+ },
+ },
+ }, {
+ headers: {
+ "content-type": "application/strategic-merge-patch+json",
+ },
+ });
+ }
+}
+
+export interface Scale {
+ apiVersion: "autoscaling/v1";
+ kind: "Scale";
+ metadata: KubeObjectMetadata;
+ spec: {
+ replicas: number;
+ };
+ status: {
+ replicas: number;
+ selector: string;
+ };
+}
+
+export interface ReplicationControllerSpec {
+ /**
+ * Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available.
+ * Defaults to 0 (pod will be considered available as soon as it is ready)
+ */
+ minReadySeconds?: number;
+ /**
+ * Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified.
+ * Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
+ */
+ replicas?: number;
+ /**
+ * Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template.
+ * Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template.
+ * More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
+ */
+ selector?: Record;
+ /**
+ * Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef.
+ * More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template
+ */
+ template: PodTemplateSpec;
+}
+
+export interface ReplicationControllerStatus extends KubeObjectStatus {
+ /**
+ * The number of available replicas (ready for at least minReadySeconds) for this replication controller.
+ */
+ availableReplicas: number;
+ /**
+ * The number of pods that have labels matching the labels of the pod template of the replication controller.
+ */
+ fullyLabeledReplicas: number;
+ /**
+ * ObservedGeneration reflects the generation of the most recently observed replication controller.
+ */
+ observedGeneration: number;
+ /**
+ * The number of ready replicas for this replication controller.
+ */
+ readyReplicas: number;
+ /**
+ * Replicas is the most recently observed number of replicas.
+ * More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
+ */
+ replicas: number;
+}
+
+export class ReplicationController extends KubeObject<
+ NamespaceScopedMetadata,
+ ReplicationControllerStatus,
+ ReplicationControllerSpec
+> {
+ static kind = "ReplicationController";
+ static namespaced = true;
+ static apiBase = "/api/v1/replicationcontrollers";
+
+ getMinReadySeconds(): number {
+ return this.spec?.minReadySeconds ?? 0;
+ }
+
+ getGeneration() {
+ return this.status?.observedGeneration;
+ }
+
+ getSelectorLabels(): string[] {
+ return KubeObject.stringifyLabels(this.spec.selector);
+ }
+
+ getReplicas(): number | undefined {
+ return this.status?.replicas;
+ }
+
+ getDesiredReplicas(): number {
+ return this.spec?.replicas ?? 0;
+ }
+
+ getAvailableReplicas(): number | undefined {
+ return this.status?.availableReplicas;
+ }
+
+ getLabeledReplicas(): number | undefined {
+ return this.status?.fullyLabeledReplicas;
+ }
+
+ getConditions(): BaseKubeObjectCondition[] {
+ return this.status?.conditions ?? [];
+ }
+}
diff --git a/packages/core/src/common/rbac.ts b/packages/core/src/common/rbac.ts
index e2ccad3806..03b4fd1de9 100644
--- a/packages/core/src/common/rbac.ts
+++ b/packages/core/src/common/rbac.ts
@@ -6,7 +6,7 @@
export type KubeResource =
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" |
"secrets" | "configmaps" | "ingresses" | "ingressclasses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |
- "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" |
+ "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "replicationcontrollers" | "jobs" | "cronjobs" |
"endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "verticalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" |
"priorityclasses" | "runtimeclasses" |
"roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts";
@@ -171,6 +171,11 @@ export const apiResourceRecord: Record = {
group: "apps",
namespaced: true,
},
+ replicationcontrollers: {
+ kind: "ReplicationController",
+ group: "", // core
+ namespaced: true,
+ },
roles: {
kind: "Role",
group: "rbac.authorization.k8s.io",
diff --git a/packages/core/src/extensions/ipc/ipc-main.ts b/packages/core/src/extensions/ipc/ipc-main.ts
index cd18bcc56c..8bb0ada63d 100644
--- a/packages/core/src/extensions/ipc/ipc-main.ts
+++ b/packages/core/src/extensions/ipc/ipc-main.ts
@@ -27,12 +27,12 @@ export abstract class IpcMain extends IpcRegistrar {
listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer {
const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;
const cleanup = once(() => {
- this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
+ this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
return ipcMain.removeListener(prefixedChannel, listener);
});
- this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
+ this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
ipcMain.addListener(prefixedChannel, listener);
this.extension[Disposers].push(cleanup);
@@ -47,10 +47,10 @@ export abstract class IpcMain extends IpcRegistrar {
handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void {
const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;
- this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
+ this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
ipcMainHandle(prefixedChannel, handler);
this.extension[Disposers].push(() => {
- this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
+ this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
return ipcMain.removeHandler(prefixedChannel);
});
diff --git a/packages/core/src/extensions/ipc/ipc-renderer.ts b/packages/core/src/extensions/ipc/ipc-renderer.ts
index 71b67779f5..ff61738113 100644
--- a/packages/core/src/extensions/ipc/ipc-renderer.ts
+++ b/packages/core/src/extensions/ipc/ipc-renderer.ts
@@ -28,12 +28,12 @@ export abstract class IpcRenderer extends IpcRegistrar {
listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer {
const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;
const cleanup = once(() => {
- console.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
+ console.debug(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
return ipcRenderer.removeListener(prefixedChannel, listener);
});
- console.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
+ console.debug(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
ipcRenderer.addListener(prefixedChannel, listener);
this.extension[Disposers].push(cleanup);
diff --git a/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/about-bundled-extensions.injectable.ts b/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/about-bundled-extensions.injectable.ts
new file mode 100644
index 0000000000..449ce5f4a8
--- /dev/null
+++ b/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/about-bundled-extensions.injectable.ts
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { bundledExtensionInjectionToken } from "../../../../../../common/library";
+import buildSemanticVersionInjectable from "../../../../../../common/vars/build-semantic-version.injectable";
+
+const aboutBundledExtensionsInjectable = getInjectable({
+ id: "about-bundled-extensions",
+ instantiate: (di) => {
+ const buildSemanticVersion = di.inject(buildSemanticVersionInjectable);
+ const bundledExtensions = di.injectMany(bundledExtensionInjectionToken);
+
+ if (buildSemanticVersion.get().prerelease[0] === "latest") {
+ return [];
+ }
+
+ return bundledExtensions.map(ext => `${ext.manifest.name}: ${ext.manifest.version}`);
+ },
+});
+
+export default aboutBundledExtensionsInjectable;
diff --git a/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/show-about.injectable.ts b/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/show-about.injectable.ts
index a91e2af337..8bc78ae8c3 100644
--- a/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/show-about.injectable.ts
+++ b/packages/core/src/features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/show-about.injectable.ts
@@ -10,6 +10,7 @@ import productNameInjectable from "../../../../../../common/vars/product-name.in
import buildVersionInjectable from "../../../../../../main/vars/build-version/build-version.injectable";
import extensionApiVersionInjectable from "../../../../../../common/vars/extension-api-version.injectable";
import applicationCopyrightInjectable from "../../../../../../common/vars/application-copyright.injectable";
+import aboutBundledExtensionsInjectable from "./about-bundled-extensions.injectable";
const showAboutInjectable = getInjectable({
id: "show-about",
@@ -22,6 +23,7 @@ const showAboutInjectable = getInjectable({
const appName = di.inject(appNameInjectable);
const productName = di.inject(productNameInjectable);
const applicationCopyright = di.inject(applicationCopyrightInjectable);
+ const aboutBundledExtensions = di.inject(aboutBundledExtensionsInjectable);
return () => {
const appInfo = [
@@ -30,6 +32,7 @@ const showAboutInjectable = getInjectable({
`Electron: ${process.versions.electron}`,
`Chrome: ${process.versions.chrome}`,
`Node: ${process.versions.node}`,
+ ...aboutBundledExtensions,
applicationCopyright,
];
diff --git a/packages/core/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap b/packages/core/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap
index 86e80acc4c..c36ac69784 100644
--- a/packages/core/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap
+++ b/packages/core/src/features/helm-releases/__snapshots__/showing-details-for-helm-release.test.ts.snap
@@ -5818,7 +5818,7 @@ exports[`showing details for helm release given application is started when navi
@@ -7100,7 +7100,7 @@ exports[`showing details for helm release given application is started when navi
@@ -8382,7 +8382,7 @@ exports[`showing details for helm release given application is started when navi
@@ -9469,7 +9469,7 @@ exports[`showing details for helm release given application is started when navi
@@ -10557,7 +10557,7 @@ exports[`showing details for helm release given application is started when navi
@@ -11840,7 +11840,7 @@ exports[`showing details for helm release given application is started when navi
@@ -17047,3 +17047,2077 @@ exports[`showing details for helm release given application is started when navi
+
+
+
+
+
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-other-name
+
+
+ content_copy
+
+
+
+ Copy
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-other-name
+
+
+ content_copy
+
+
+
+ Copy
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+