diff --git a/.eslintrc.js b/.eslintrc.js
index 52ba26dcb3..09a06987c4 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -11,7 +11,6 @@ module.exports = {
"**/dist/**/*",
"**/static/**/*",
"**/site/**/*",
- "extensions/*/*.tgz",
"build/webpack/**/*",
],
settings: {
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ab7ee4c273..2c5d84bf0e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,6 +33,6 @@ jobs:
publish-npm:
uses: ./.github/workflows/publish-release-npm.yml
needs: release
- if: ${{ jobs.release.outputs.version != '' }}
+ if: ${{ needs.release.outputs.version != '' }}
with:
- version: ${{ jobs.release.outputs.version }}
+ version: ${{ needs.release.outputs.version }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ee1ab58970..e71bfb0be8 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -57,10 +57,6 @@ jobs:
name: Run tests
if: ${{ matrix.type == 'unit' }}
- - run: make test-extensions
- name: Run In-tree Extension tests
- if: ${{ matrix.type == 'unit' }}
-
- run: make ci-validate-dev
if: ${{ contains(github.event.pull_request.labels.*.name, 'dependencies') && matrix.type == 'unit' }}
name: Validate dev mode will work
diff --git a/.idea/lens.iml b/.idea/lens.iml
index 88175e2aaa..3bef0f9888 100644
--- a/.idea/lens.iml
+++ b/.idea/lens.iml
@@ -6,10 +6,6 @@
-
-
-
-
@@ -20,4 +16,4 @@
-
\ No newline at end of file
+
diff --git a/Makefile b/Makefile
index 0308d23b9a..a6870a4816 100644
--- a/Makefile
+++ b/Makefile
@@ -5,10 +5,6 @@ CMD_ARGS = $(filter-out $@,$(MAKECMDGOALS))
NPM_RELEASE_TAG ?= latest
ELECTRON_BUILDER_EXTRA_ARGS ?=
-EXTENSIONS_DIR = ./extensions
-extensions = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir})
-extension_node_modules = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir}/node_modules)
-extension_dists = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir}/dist)
ifeq ($(OS),Windows_NT)
DETECTED_OS := Windows
@@ -28,10 +24,10 @@ compile-dev: node_modules
yarn compile:renderer --cache
.PHONY: validate-dev
-ci-validate-dev: binaries/client build-extensions compile-dev
+ci-validate-dev: binaries/client compile-dev
.PHONY: dev
-dev: binaries/client build-extensions
+dev: binaries/client
rm -rf static/build/
yarn run build:tray-icons
yarn dev
@@ -54,7 +50,6 @@ integration: build
.PHONY: build
build: node_modules binaries/client
- $(MAKE) build-extensions -B
yarn run build:tray-icons
yarn run compile
ifeq "$(DETECTED_OS)" "Windows"
@@ -63,28 +58,8 @@ ifeq "$(DETECTED_OS)" "Windows"
endif
yarn run electron-builder --publish onTag $(ELECTRON_BUILDER_EXTRA_ARGS)
-.NOTPARALLEL: $(extension_node_modules)
-$(extension_node_modules): node_modules
- cd $(@:/node_modules=) && ../../node_modules/.bin/npm install --no-audit --no-fund --no-save
-
-$(extension_dists): packages/extensions/dist $(extension_node_modules)
- cd $(@:/dist=) && ../../node_modules/.bin/npm run build
- rm -rf ./node_modules/$(shell basename $(@:/dist=))
-
-.PHONY: clean-old-extensions
-clean-old-extensions:
- find ./extensions -mindepth 1 -maxdepth 1 -type d '!' -exec test -e '{}/package.json' \; -exec rm -rf {} \;
-
-.PHONY: build-extensions
-build-extensions: node_modules clean-old-extensions $(extension_dists)
- yarn install --check-files --frozen-lockfile --network-timeout=100000
-
-.PHONY: test-extensions
-test-extensions: $(extension_node_modules)
- $(foreach dir, $(extensions), (cd $(dir) && npm run test || exit $?);)
-
-packages/extensions/__mocks__:
- cp -r __mocks__ packages/extensions/
+src/extensions/npm/extensions/__mocks__:
+ cp -r __mocks__ src/extensions/npm/extensions/
packages/extensions/dist: packages/extensions/node_modules
yarn compile:extension-types
@@ -121,17 +96,13 @@ build-docs:
docs: build-docs
yarn mkdocs-serve-local
-.PHONY: clean-extensions
-clean-extensions:
- rm -rf $(EXTENSIONS_DIR)/*/{dist,node_modules,*.tgz}
-
.PHONY: clean-npm
clean-npm:
rm -rf packages/extensions/{dist,__mocks__,node_modules}
rm -rf static/build/library/
.PHONY: clean
-clean: clean-npm clean-extensions
+clean: clean-npm
rm -rf binaries/client
rm -rf dist
rm -rf static/build
diff --git a/extensions/.eslintrc.js b/extensions/.eslintrc.js
deleted file mode 100644
index 9d7b71763d..0000000000
--- a/extensions/.eslintrc.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-module.exports = {
- "overrides": [
- {
- files: [
- "**/*.ts",
- "**/*.tsx",
- ],
- rules: {
- "import/no-unresolved": ["error", {
- ignore: ["@k8slens/extensions"],
- }],
- },
- },
- ],
-};
diff --git a/extensions/.gitignore b/extensions/.gitignore
deleted file mode 100644
index 193cfa4791..0000000000
--- a/extensions/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*/*.tgz
diff --git a/extensions/kube-object-event-status/Makefile b/extensions/kube-object-event-status/Makefile
deleted file mode 100644
index 1c9223e184..0000000000
--- a/extensions/kube-object-event-status/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-install-deps:
- npm install
-
-build: install-deps
- npm run build
-
-test:
- npm run test
diff --git a/extensions/kube-object-event-status/package.json b/extensions/kube-object-event-status/package.json
deleted file mode 100644
index 4e65471429..0000000000
--- a/extensions/kube-object-event-status/package.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "kube-object-event-status",
- "version": "6.1.1",
- "description": "Adds kube object status from events",
- "renderer": "dist/renderer.js",
- "lens": {
- "metadata": {},
- "styles": []
- },
- "scripts": {
- "build": "npx webpack",
- "dev": "npx webpack -- --watch",
- "test": "echo NO TESTS"
- },
- "files": [
- "dist/**/*"
- ],
- "devDependencies": {
- "@k8slens/extensions": "file:../../src/extensions/npm/extensions"
- }
-}
diff --git a/extensions/kube-object-event-status/renderer.tsx b/extensions/kube-object-event-status/renderer.tsx
deleted file mode 100644
index 65b92a2658..0000000000
--- a/extensions/kube-object-event-status/renderer.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { Renderer } from "@k8slens/extensions";
-import { resolveStatus, resolveStatusForCronJobs, resolveStatusForPods } from "./src/resolver";
-
-export default class EventResourceStatusRendererExtension extends Renderer.LensExtension {
- kubeObjectStatusTexts = [
- {
- kind: "Pod",
- apiVersions: ["v1"],
- resolve: (pod: Renderer.K8sApi.Pod) => resolveStatusForPods(pod),
- },
- {
- kind: "ReplicaSet",
- apiVersions: ["v1"],
- resolve: (replicaSet: Renderer.K8sApi.ReplicaSet) => resolveStatus(replicaSet),
- },
- {
- kind: "Deployment",
- apiVersions: ["apps/v1"],
- resolve: (deployment: Renderer.K8sApi.Deployment) => resolveStatus(deployment),
- },
- {
- kind: "StatefulSet",
- apiVersions: ["apps/v1"],
- resolve: (statefulSet: Renderer.K8sApi.StatefulSet) => resolveStatus(statefulSet),
- },
- {
- kind: "DaemonSet",
- apiVersions: ["apps/v1"],
- resolve: (daemonSet: Renderer.K8sApi.DaemonSet) => resolveStatus(daemonSet),
- },
- {
- kind: "Job",
- apiVersions: [
- "batch/v1",
- "batch/v1beta1",
- ],
- resolve: (job: Renderer.K8sApi.Job) => resolveStatus(job),
- },
- {
- kind: "CronJob",
- apiVersions: [
- "batch/v1",
- "batch/v1beta1",
- ],
- resolve: (cronJob: Renderer.K8sApi.CronJob) => resolveStatusForCronJobs(cronJob),
- },
- ];
-}
diff --git a/extensions/kube-object-event-status/src/resolver.tsx b/extensions/kube-object-event-status/src/resolver.tsx
deleted file mode 100644
index fa98290c49..0000000000
--- a/extensions/kube-object-event-status/src/resolver.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { Renderer } from "@k8slens/extensions";
-
-const { apiManager, eventApi, KubeObjectStatusLevel } = Renderer.K8sApi;
-
-type KubeObject = Renderer.K8sApi.KubeObject;
-type Pod = Renderer.K8sApi.Pod;
-type CronJob = Renderer.K8sApi.CronJob;
-type KubeObjectStatus = Renderer.K8sApi.KubeObjectStatus;
-type EventStore = Renderer.K8sApi.EventStore;
-
-export function resolveStatus(object: KubeObject): KubeObjectStatus {
- const eventStore = apiManager.getStore(eventApi);
- const events = (eventStore as EventStore).getEventsByObject(object);
- const warnings = events.filter(evt => evt.isWarning());
-
- if (!events.length || !warnings.length) {
- return null;
- }
- const event = [...warnings, ...events][0]; // get latest event
-
- return {
- level: KubeObjectStatusLevel.WARNING,
- text: `${event.message}`,
- timestamp: event.metadata.creationTimestamp,
- };
-}
-
-export function resolveStatusForPods(pod: Pod): KubeObjectStatus {
- if (!pod.hasIssues()) {
- return null;
- }
- const eventStore = apiManager.getStore(eventApi);
- const events = (eventStore as EventStore).getEventsByObject(pod);
- const warnings = events.filter(evt => evt.isWarning());
-
- if (!events.length || !warnings.length) {
- return null;
- }
- const event = [...warnings, ...events][0]; // get latest event
-
- return {
- level: KubeObjectStatusLevel.WARNING,
- text: `${event.message}`,
- timestamp: event.metadata.creationTimestamp,
- };
-}
-
-export function resolveStatusForCronJobs(cronJob: CronJob): KubeObjectStatus {
- const eventStore = apiManager.getStore(eventApi);
- let events = (eventStore as EventStore).getEventsByObject(cronJob);
- const warnings = events.filter(evt => evt.isWarning());
-
- if (cronJob.isNeverRun()) {
- events = events.filter(event => event.reason != "FailedNeedsStart");
- }
-
- if (!events.length || !warnings.length) {
- return null;
- }
- const event = [...warnings, ...events][0]; // get latest event
-
- return {
- level: KubeObjectStatusLevel.WARNING,
- text: `${event.message}`,
- timestamp: event.metadata.creationTimestamp,
- };
-}
diff --git a/extensions/kube-object-event-status/tsconfig.json b/extensions/kube-object-event-status/tsconfig.json
deleted file mode 100644
index 5b6c61577e..0000000000
--- a/extensions/kube-object-event-status/tsconfig.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "outDir": "dist",
- "module": "CommonJS",
- "target": "ES2017",
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
- "moduleResolution": "Node",
- "sourceMap": false,
- "declaration": false,
- "strict": false,
- "noImplicitAny": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "experimentalDecorators": true,
- "useDefineForClassFields": true,
- "jsx": "react"
- },
- "include": [
- "./*.ts",
- "./*.tsx"
- ],
- "exclude": [
- "node_modules",
- "*.js"
- ]
-}
diff --git a/extensions/kube-object-event-status/webpack.config.js b/extensions/kube-object-event-status/webpack.config.js
deleted file mode 100644
index 8fe82e48a7..0000000000
--- a/extensions/kube-object-event-status/webpack.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-const path = require("path");
-
-module.exports = [
- {
- entry: "./renderer.tsx",
- context: __dirname,
- target: "electron-renderer",
- mode: "production",
- optimization: {
- minimize: false,
- },
- module: {
- rules: [
- {
- test: /\.tsx?$/,
- use: "ts-loader",
- exclude: /node_modules/,
- },
- ],
- },
- externals: [
- {
- "@k8slens/extensions": "var global.LensExtensions",
- "react": "var global.React",
- "react-dom": "var global.ReactDOM",
- "mobx": "var global.Mobx",
- "mobx-react": "var global.MobxReact",
- },
- ],
- resolve: {
- extensions: [ ".tsx", ".ts", ".js" ],
- },
- output: {
- libraryTarget: "commonjs2",
- globalObject: "this",
- filename: "renderer.js",
- path: path.resolve(__dirname, "dist"),
- },
- },
-];
diff --git a/extensions/metrics-cluster-feature/package.json b/extensions/metrics-cluster-feature/package.json
deleted file mode 100644
index 3be7b58d58..0000000000
--- a/extensions/metrics-cluster-feature/package.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "name": "lens-metrics-cluster-feature",
- "version": "6.1.0",
- "description": "Lens metrics cluster feature",
- "renderer": "dist/renderer.js",
- "lens": {
- "metadata": {},
- "styles": []
- },
- "scripts": {
- "build": "npx webpack",
- "dev": "npx webpack -- --watch",
- "test": "npx jest --passWithNoTests --env=jsdom src $@",
- "clean": "rm -rf dist/ && rm *.tgz"
- },
- "files": [
- "dist/**/*",
- "resources/**/*"
- ],
- "devDependencies": {
- "@k8slens/extensions": "file:../../src/extensions/npm/extensions",
- "semver": "^7.3.2"
- }
-}
diff --git a/extensions/metrics-cluster-feature/renderer.tsx b/extensions/metrics-cluster-feature/renderer.tsx
deleted file mode 100644
index 8b04ca0b8f..0000000000
--- a/extensions/metrics-cluster-feature/renderer.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import React from "react";
-import type { Common } from "@k8slens/extensions";
-import { Renderer } from "@k8slens/extensions";
-import { MetricsSettings } from "./src/metrics-settings";
-
-export default class ClusterMetricsFeatureExtension extends Renderer.LensExtension {
- entitySettings = [
- {
- apiVersions: ["entity.k8slens.dev/v1alpha1"],
- kind: "KubernetesCluster",
- title: "Lens Metrics",
- priority: 5,
- components: {
- View: ({ entity = null }: { entity: Common.Catalog.KubernetesCluster }) => {
- return (
-
- );
- },
- },
- },
- ];
-}
diff --git a/extensions/metrics-cluster-feature/resources/01-namespace.yml.hb b/extensions/metrics-cluster-feature/resources/01-namespace.yml.hb
deleted file mode 100644
index dd3816fdff..0000000000
--- a/extensions/metrics-cluster-feature/resources/01-namespace.yml.hb
+++ /dev/null
@@ -1,6 +0,0 @@
-apiVersion: v1
-kind: Namespace
-metadata:
- name: lens-metrics
- annotations:
- extensionVersion: "{{ version }}"
diff --git a/extensions/metrics-cluster-feature/resources/02-configmap.yml.hb b/extensions/metrics-cluster-feature/resources/02-configmap.yml.hb
deleted file mode 100644
index 582052bd87..0000000000
--- a/extensions/metrics-cluster-feature/resources/02-configmap.yml.hb
+++ /dev/null
@@ -1,281 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: prometheus-config
- namespace: lens-metrics
-data:
- prometheus.yaml: |-
- # Global config
- global:
- scrape_interval: 15s
-
- {{#if alertManagers}}
- # AlertManager
- alerting:
- alertmanagers:
- - static_configs:
- - targets:
- {{#each alertManagers}}
- - {{this}}
- {{/each}}
- {{/if}}
-
- # Scrape configs for running Prometheus on a Kubernetes cluster.
- # This uses separate scrape configs for cluster components (i.e. API server, node)
- # and services to allow each to use different authentication configs.
- #
- # Kubernetes labels will be added as Prometheus labels on metrics via the
- # `labelmap` relabeling action.
- scrape_configs:
-
- # Scrape config for API servers.
- #
- # Kubernetes exposes API servers as endpoints to the default/kubernetes
- # service so this uses `endpoints` role and uses relabelling to only keep
- # the endpoints associated with the default/kubernetes service using the
- # default named port `https`. This works for single API server deployments as
- # well as HA API server deployments.
- - job_name: 'kubernetes-apiservers'
- kubernetes_sd_configs:
- - role: endpoints
-
- scheme: https
- tls_config:
- ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- # Using endpoints to discover kube-apiserver targets finds the pod IP
- # (host IP since apiserver uses host network) which is not used in
- # the server certificate.
- insecure_skip_verify: true
- bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
-
- # Keep only the default/kubernetes service endpoints for the https port. This
- # will add targets for each API server which Kubernetes adds an endpoint to
- # the default/kubernetes service.
- relabel_configs:
- - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
- action: keep
- regex: default;kubernetes;https
- - replacement: apiserver
- action: replace
- target_label: job
-
- # Scrape config for node (i.e. kubelet) /metrics (e.g. 'kubelet_'). Explore
- # metrics from a node by scraping kubelet (127.0.0.1:10250/metrics).
- - job_name: 'kubelet'
- kubernetes_sd_configs:
- - role: node
-
- scheme: https
- tls_config:
- ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- # Kubelet certs don't have any fixed IP SANs
- insecure_skip_verify: true
- bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
-
- relabel_configs:
- - action: labelmap
- regex: __meta_kubernetes_node_label_(.+)
- - replacement: 'lens-metrics'
- target_label: kubernetes_namespace
-
- metric_relabel_configs:
- - source_labels:
- - namespace
- action: replace
- regex: (.+)
- target_label: kubernetes_namespace
-
- # Scrape config for Kubelet cAdvisor. Explore metrics from a node by
- # scraping kubelet (127.0.0.1:10250/metrics/cadvisor).
- - job_name: 'kubernetes-cadvisor'
- kubernetes_sd_configs:
- - role: node
-
- scheme: https
- metrics_path: /metrics/cadvisor
- tls_config:
- # Kubelet certs don't have any fixed IP SANs
- insecure_skip_verify: true
- ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
-
- relabel_configs:
- - action: labelmap
- regex: __meta_kubernetes_node_label_(.+)
- metric_relabel_configs:
- - source_labels:
- - namespace
- action: replace
- target_label: kubernetes_namespace
- - source_labels:
- - pod
- regex: (.*)
- replacement: $1
- action: replace
- target_label: pod_name
- - source_labels:
- - container
- regex: (.*)
- replacement: $1
- action: replace
- target_label: container_name
-
- # Scrap etcd metrics from masters via etcd-scraper-proxy
- - job_name: 'etcd'
- kubernetes_sd_configs:
- - role: pod
- scheme: http
- relabel_configs:
- - source_labels: [__meta_kubernetes_namespace]
- action: keep
- regex: 'kube-system'
- - source_labels: [__meta_kubernetes_pod_label_component]
- action: keep
- regex: 'etcd-scraper-proxy'
- - action: labelmap
- regex: __meta_kubernetes_pod_label_(.+)
-
- # Scrape config for service endpoints.
- #
- # The relabeling allows the actual service scrape endpoint to be configured
- # via the following annotations:
- #
- # * `prometheus.io/scrape`: Only scrape services that have a value of `true`
- # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need
- # to set this to `https` & most likely set the `tls_config` of the scrape config.
- # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
- # * `prometheus.io/port`: If the metrics are exposed on a different port to the
- # service then set this appropriately.
- - job_name: 'kubernetes-service-endpoints'
-
- kubernetes_sd_configs:
- - role: endpoints
- namespaces:
- names:
- - lens-metrics
-
- relabel_configs:
- - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
- action: keep
- regex: true
- - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
- action: replace
- target_label: __scheme__
- regex: (https?)
- - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
- action: replace
- target_label: __metrics_path__
- regex: (.+)
- - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
- action: replace
- target_label: __address__
- regex: ([^:]+)(?::\d+)?;(\d+)
- replacement: $1:$2
- - action: labelmap
- regex: __meta_kubernetes_service_label_(.+)
- - source_labels: [__meta_kubernetes_service_name]
- action: replace
- target_label: job
- - action: replace
- source_labels:
- - __meta_kubernetes_pod_node_name
- target_label: kubernetes_node
- - source_labels: [__meta_kubernetes_namespace]
- action: replace
- target_label: kubernetes_namespace
- metric_relabel_configs:
- - source_labels:
- - namespace
- action: replace
- regex: (.+)
- target_label: kubernetes_namespace
-
- # Example scrape config for probing services via the Blackbox Exporter.
- #
- # The relabeling allows the actual service scrape endpoint to be configured
- # via the following annotations:
- #
- # * `prometheus.io/probe`: Only probe services that have a value of `true`
- - job_name: 'kubernetes-services'
-
- metrics_path: /probe
- params:
- module: [http_2xx]
-
- kubernetes_sd_configs:
- - role: service
- namespaces:
- names:
- - lens-metrics
-
- relabel_configs:
- - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
- action: keep
- regex: true
- - source_labels: [__address__]
- target_label: __param_target
- - target_label: __address__
- replacement: blackbox
- - source_labels: [__param_target]
- target_label: instance
- - action: labelmap
- regex: __meta_kubernetes_service_label_(.+)
- - source_labels: [__meta_kubernetes_service_name]
- target_label: job
- metric_relabel_configs:
- - source_labels:
- - namespace
- action: replace
- regex: (.+)
- target_label: kubernetes_namespace
-
- # Example scrape config for pods
- #
- # The relabeling allows the actual pod scrape endpoint to be configured via the
- # following annotations:
- #
- # * `prometheus.io/scrape`: Only scrape pods that have a value of `true`
- # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
- # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the
- # pod's declared ports (default is a port-free target if none are declared).
- - job_name: 'kubernetes-pods'
-
- kubernetes_sd_configs:
- - role: pod
- namespaces:
- names:
- - lens-metrics
-
- relabel_configs:
- - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
- action: keep
- regex: true
- - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
- action: replace
- target_label: __metrics_path__
- regex: (.+)
- - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
- action: replace
- regex: ([^:]+)(?::\d+)?;(\d+)
- replacement: $1:$2
- target_label: __address__
- - action: labelmap
- regex: __meta_kubernetes_pod_label_(.+)
- - source_labels: [__meta_kubernetes_namespace]
- action: replace
- target_label: kubernetes_namespace
- - source_labels: [__meta_kubernetes_pod_name]
- action: replace
- target_label: kubernetes_pod_name
- metric_relabel_configs:
- - source_labels:
- - namespace
- action: replace
- regex: (.+)
- target_label: kubernetes_namespace
-
- # Rule files
- rule_files:
- - "/etc/prometheus/rules/*.rules"
- - "/etc/prometheus/rules/*.yaml"
- - "/etc/prometheus/rules/*.yml"
diff --git a/extensions/metrics-cluster-feature/resources/02-service-account.yml b/extensions/metrics-cluster-feature/resources/02-service-account.yml
deleted file mode 100644
index 5cb45a352f..0000000000
--- a/extensions/metrics-cluster-feature/resources/02-service-account.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
- name: prometheus
- namespace: lens-metrics
diff --git a/extensions/metrics-cluster-feature/resources/03-service.yml.hb b/extensions/metrics-cluster-feature/resources/03-service.yml.hb
deleted file mode 100644
index 3cdcdbc260..0000000000
--- a/extensions/metrics-cluster-feature/resources/03-service.yml.hb
+++ /dev/null
@@ -1,18 +0,0 @@
-{{#if prometheus.enabled}}
-apiVersion: v1
-kind: Service
-metadata:
- name: prometheus
- namespace: lens-metrics
- annotations:
- prometheus.io/scrape: 'true'
-spec:
- type: ClusterIP
- selector:
- name: prometheus
- ports:
- - name: web
- protocol: TCP
- port: 80
- targetPort: 9090
-{{/if}}
diff --git a/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb b/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb
deleted file mode 100644
index 288cd553b1..0000000000
--- a/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb
+++ /dev/null
@@ -1,113 +0,0 @@
-{{#if prometheus.enabled}}
-apiVersion: apps/v1
-kind: StatefulSet
-metadata:
- name: prometheus
- namespace: lens-metrics
-spec:
- replicas: {{replicas}}
- serviceName: prometheus
- selector:
- matchLabels:
- name: prometheus
- template:
- metadata:
- labels:
- name: prometheus
- spec:
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/os
- operator: In
- values:
- - linux
- # <%- if config.node_selector -%>
- # nodeSelector:
- # <%- node_selector.to_h.each do |key, value| -%>
- # <%= key %>: <%= value %>
- # <%- end -%>
- # <%- end -%>
- # <%- unless config.tolerations.empty? -%>
- # tolerations:
- # <%- config.tolerations.each do |t| -%>
- # -
- # <%- t.each do |k,v| -%>
- # <%= k %>: <%= v %>
- # <%- end -%>
- # <%- end -%>
- # <%- end -%>
- serviceAccountName: prometheus
- initContainers:
- - name: chown
- image: docker.io/alpine:3.12
- command: ["chown", "-R", "65534:65534", "/var/lib/prometheus"]
- volumeMounts:
- - name: data
- mountPath: /var/lib/prometheus
- containers:
- - name: prometheus
- image: quay.io/prometheus/prometheus:v2.27.1
- args:
- - --web.listen-address=0.0.0.0:9090
- - --config.file=/etc/prometheus/prometheus.yaml
- - --storage.tsdb.path=/var/lib/prometheus
- - --storage.tsdb.retention.time={{retention.time}}
- - --storage.tsdb.retention.size={{retention.size}}
- - --storage.tsdb.min-block-duration=2h
- - --storage.tsdb.max-block-duration=2h
- ports:
- - name: web
- containerPort: 9090
- volumeMounts:
- - name: config
- mountPath: /etc/prometheus
- - name: rules
- mountPath: /etc/prometheus/rules
- - name: data
- mountPath: /var/lib/prometheus
- readinessProbe:
- httpGet:
- path: /-/ready
- port: 9090
- initialDelaySeconds: 10
- timeoutSeconds: 10
- livenessProbe:
- httpGet:
- path: /-/healthy
- port: 9090
- initialDelaySeconds: 10
- timeoutSeconds: 10
- resources:
- requests:
- cpu: 100m
- memory: 512Mi
- terminationGracePeriodSeconds: 30
- volumes:
- - name: config
- configMap:
- name: prometheus-config
- - name: rules
- configMap:
- name: prometheus-rules
- {{#unless persistence.enabled}}
- - name: data
- emptyDir: {}
- {{/unless}}
- {{#if persistence.enabled}}
- volumeClaimTemplates:
- - metadata:
- name: data
- spec:
- accessModes:
- - ReadWriteOnce
- {{#if persistence.storageClass}}
- storageClassName: "{{persistence.storageClass}}"
- {{/if}}
- resources:
- requests:
- storage: {{persistence.size}}
- {{/if}}
-{{/if}}
diff --git a/extensions/metrics-cluster-feature/resources/04-rules.yml b/extensions/metrics-cluster-feature/resources/04-rules.yml
deleted file mode 100644
index b7f088a003..0000000000
--- a/extensions/metrics-cluster-feature/resources/04-rules.yml
+++ /dev/null
@@ -1,514 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: prometheus-rules
- namespace: lens-metrics
-data:
- alertmanager.rules.yaml: |
- groups:
- - name: alertmanager.rules
- rules:
- - alert: AlertmanagerConfigInconsistent
- expr: count_values("config_hash", alertmanager_config_hash) BY (service) / ON(service)
- GROUP_LEFT() label_replace(prometheus_operator_alertmanager_spec_replicas, "service",
- "alertmanager-$1", "alertmanager", "(.*)") != 1
- for: 5m
- labels:
- severity: critical
- annotations:
- description: The configuration of the instances of the Alertmanager cluster
- `{{$labels.service}}` are out of sync.
- - alert: AlertmanagerDownOrMissing
- expr: label_replace(prometheus_operator_alertmanager_spec_replicas, "job", "alertmanager-$1",
- "alertmanager", "(.*)") / ON(job) GROUP_RIGHT() sum(up) BY (job) != 1
- for: 5m
- labels:
- severity: warning
- annotations:
- description: An unexpected number of Alertmanagers are scraped or Alertmanagers
- disappeared from discovery.
- - alert: AlertmanagerFailedReload
- expr: alertmanager_config_last_reload_successful == 0
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Reloading Alertmanager's configuration has failed for {{ $labels.namespace
- }}/{{ $labels.pod}}.
- etcd3.rules.yaml: |
- groups:
- - name: ./etcd3.rules
- rules:
- - alert: InsufficientMembers
- expr: count(up{job="etcd"} == 0) > (count(up{job="etcd"}) / 2 - 1)
- for: 3m
- labels:
- severity: critical
- annotations:
- description: If one more etcd member goes down the cluster will be unavailable
- summary: etcd cluster insufficient members
- - alert: NoLeader
- expr: etcd_server_has_leader{job="etcd"} == 0
- for: 1m
- labels:
- severity: critical
- annotations:
- description: etcd member {{ $labels.instance }} has no leader
- summary: etcd member has no leader
- - alert: HighNumberOfLeaderChanges
- expr: increase(etcd_server_leader_changes_seen_total{job="etcd"}[1h]) > 3
- labels:
- severity: warning
- annotations:
- description: etcd instance {{ $labels.instance }} has seen {{ $value }} leader
- changes within the last hour
- summary: a high number of leader changes within the etcd cluster are happening
- - alert: GRPCRequestsSlow
- expr: histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{job="etcd",grpc_type="unary"}[5m])) by (grpc_service, grpc_method, le))
- > 0.15
- for: 10m
- labels:
- severity: critical
- annotations:
- description: on etcd instance {{ $labels.instance }} gRPC requests to {{ $labels.grpc_method
- }} are slow
- summary: slow gRPC requests
- - alert: HighNumberOfFailedHTTPRequests
- expr: sum(rate(etcd_http_failed_total{job="etcd"}[5m])) BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m]))
- BY (method) > 0.01
- for: 10m
- labels:
- severity: warning
- annotations:
- description: '{{ $value }}% of requests for {{ $labels.method }} failed on etcd
- instance {{ $labels.instance }}'
- summary: a high number of HTTP requests are failing
- - alert: HighNumberOfFailedHTTPRequests
- expr: sum(rate(etcd_http_failed_total{job="etcd"}[5m])) BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m]))
- BY (method) > 0.05
- for: 5m
- labels:
- severity: critical
- annotations:
- description: '{{ $value }}% of requests for {{ $labels.method }} failed on etcd
- instance {{ $labels.instance }}'
- summary: a high number of HTTP requests are failing
- - alert: HTTPRequestsSlow
- expr: histogram_quantile(0.99, rate(etcd_http_successful_duration_seconds_bucket[5m]))
- > 0.15
- for: 10m
- labels:
- severity: warning
- annotations:
- description: on etcd instance {{ $labels.instance }} HTTP requests to {{ $labels.method
- }} are slow
- summary: slow HTTP requests
- - alert: EtcdMemberCommunicationSlow
- expr: histogram_quantile(0.99, rate(etcd_network_peer_round_trip_time_seconds_bucket[5m]))
- > 0.15
- for: 10m
- labels:
- severity: warning
- annotations:
- description: etcd instance {{ $labels.instance }} member communication with
- {{ $labels.To }} is slow
- summary: etcd member communication is slow
- - alert: HighNumberOfFailedProposals
- expr: increase(etcd_server_proposals_failed_total{job="etcd"}[1h]) > 5
- labels:
- severity: warning
- annotations:
- description: etcd instance {{ $labels.instance }} has seen {{ $value }} proposal
- failures within the last hour
- summary: a high number of proposals within the etcd cluster are failing
- - alert: HighFsyncDurations
- expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m]))
- > 0.5
- for: 10m
- labels:
- severity: warning
- annotations:
- description: etcd instance {{ $labels.instance }} fync durations are high
- summary: high fsync durations
- - alert: HighCommitDurations
- expr: histogram_quantile(0.99, rate(etcd_disk_backend_commit_duration_seconds_bucket[5m]))
- > 0.25
- for: 10m
- labels:
- severity: warning
- annotations:
- description: etcd instance {{ $labels.instance }} commit durations are high
- summary: high commit durations
- general.rules.yaml: |
- groups:
- - name: general.rules
- rules:
- - alert: TargetDown
- expr: 100 * (count(up == 0) BY (job) / count(up) BY (job)) > 10
- for: 10m
- labels:
- severity: warning
- annotations:
- description: '{{ $value }}% of {{ $labels.job }} targets are down.'
- summary: Targets are down
- - record: fd_utilization
- expr: process_open_fds / process_max_fds
- - alert: FdExhaustionClose
- expr: predict_linear(fd_utilization[1h], 3600 * 4) > 1
- for: 10m
- labels:
- severity: warning
- annotations:
- description: '{{ $labels.job }}: {{ $labels.namespace }}/{{ $labels.pod }} instance
- will exhaust in file/socket descriptors within the next 4 hours'
- summary: file descriptors soon exhausted
- - alert: FdExhaustionClose
- expr: predict_linear(fd_utilization[10m], 3600) > 1
- for: 10m
- labels:
- severity: critical
- annotations:
- description: '{{ $labels.job }}: {{ $labels.namespace }}/{{ $labels.pod }} instance
- will exhaust in file/socket descriptors within the next hour'
- summary: file descriptors soon exhausted
- kube-state-metrics.rules.yaml: |
- groups:
- - name: kube-state-metrics.rules
- rules:
- - alert: DeploymentGenerationMismatch
- expr: kube_deployment_status_observed_generation != kube_deployment_metadata_generation
- for: 15m
- labels:
- severity: warning
- annotations:
- description: Observed deployment generation does not match expected one for
- deployment {{$labels.namespaces}}/{{$labels.deployment}}
- summary: Deployment is outdated
- - alert: DeploymentReplicasNotUpdated
- expr: ((kube_deployment_status_replicas_updated != kube_deployment_spec_replicas)
- or (kube_deployment_status_replicas_available != kube_deployment_spec_replicas))
- unless (kube_deployment_spec_paused == 1)
- for: 15m
- labels:
- severity: warning
- annotations:
- description: Replicas are not updated and available for deployment {{$labels.namespaces}}/{{$labels.deployment}}
- summary: Deployment replicas are outdated
- - alert: DaemonSetRolloutStuck
- expr: kube_daemonset_status_number_ready / kube_daemonset_status_desired_number_scheduled
- * 100 < 100
- for: 15m
- labels:
- severity: warning
- annotations:
- description: Only {{$value}}% of desired pods scheduled and ready for daemon
- set {{$labels.namespaces}}/{{$labels.daemonset}}
- summary: DaemonSet is missing pods
- - alert: K8SDaemonSetsNotScheduled
- expr: kube_daemonset_status_desired_number_scheduled - kube_daemonset_status_current_number_scheduled
- > 0
- for: 10m
- labels:
- severity: warning
- annotations:
- description: A number of daemonsets are not scheduled.
- summary: Daemonsets are not scheduled correctly
- - alert: DaemonSetsMissScheduled
- expr: kube_daemonset_status_number_misscheduled > 0
- for: 10m
- labels:
- severity: warning
- annotations:
- description: A number of daemonsets are running where they are not supposed
- to run.
- summary: Daemonsets are not scheduled correctly
- - alert: PodFrequentlyRestarting
- expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Pod {{$labels.namespaces}}/{{$labels.pod}} restarted {{$value}}
- times within the last hour
- summary: Pod is restarting frequently
- kubelet.rules.yaml: |
- groups:
- - name: kubelet.rules
- rules:
- - alert: K8SNodeNotReady
- expr: kube_node_status_condition{condition="Ready",status="true"} == 0
- for: 1h
- labels:
- severity: warning
- annotations:
- description: The Kubelet on {{ $labels.node }} has not checked in with the API,
- or has set itself to NotReady, for more than an hour
- summary: Node status is NotReady
- - alert: K8SManyNodesNotReady
- expr: count(kube_node_status_condition{condition="Ready",status="true"} == 0)
- > 1 and (count(kube_node_status_condition{condition="Ready",status="true"} ==
- 0) / count(kube_node_status_condition{condition="Ready",status="true"})) > 0.2
- for: 1m
- labels:
- severity: critical
- annotations:
- description: '{{ $value }}% of Kubernetes nodes are not ready'
- - alert: K8SKubeletDown
- expr: count(up{job="kubelet"} == 0) / count(up{job="kubelet"}) * 100 > 3
- for: 1h
- labels:
- severity: warning
- annotations:
- description: Prometheus failed to scrape {{ $value }}% of kubelets.
- - alert: K8SKubeletDown
- expr: (absent(up{job="kubelet"} == 1) or count(up{job="kubelet"} == 0) / count(up{job="kubelet"}))
- * 100 > 10
- for: 1h
- labels:
- severity: critical
- annotations:
- description: Prometheus failed to scrape {{ $value }}% of kubelets, or all Kubelets
- have disappeared from service discovery.
- summary: Many Kubelets cannot be scraped
- - alert: K8SKubeletTooManyPods
- expr: kubelet_running_pod_count > 100
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Kubelet {{$labels.instance}} is running {{$value}} pods, close
- to the limit of 110
- summary: Kubelet is close to pod limit
- kubernetes.rules.yaml: |
- groups:
- - name: kubernetes.rules
- rules:
- - record: pod_name:container_memory_usage_bytes:sum
- expr: sum(container_memory_usage_bytes{container_name!="POD",pod_name!=""}) BY
- (pod_name)
- - record: pod_name:container_spec_cpu_shares:sum
- expr: sum(container_spec_cpu_shares{container_name!="POD",pod_name!=""}) BY (pod_name)
- - record: pod_name:container_cpu_usage:sum
- expr: sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name!=""}[5m]))
- BY (pod_name)
- - record: pod_name:container_fs_usage_bytes:sum
- expr: sum(container_fs_usage_bytes{container_name!="POD",pod_name!=""}) BY (pod_name)
- - record: namespace:container_memory_usage_bytes:sum
- expr: sum(container_memory_usage_bytes{container_name!=""}) BY (namespace)
- - record: namespace:container_spec_cpu_shares:sum
- expr: sum(container_spec_cpu_shares{container_name!=""}) BY (namespace)
- - record: namespace:container_cpu_usage:sum
- expr: sum(rate(container_cpu_usage_seconds_total{container_name!="POD"}[5m]))
- BY (namespace)
- - record: cluster:memory_usage:ratio
- expr: sum(container_memory_usage_bytes{container_name!="POD",pod_name!=""}) BY
- (cluster) / sum(machine_memory_bytes) BY (cluster)
- - record: cluster:container_spec_cpu_shares:ratio
- expr: sum(container_spec_cpu_shares{container_name!="POD",pod_name!=""}) / 1000
- / sum(machine_cpu_cores)
- - record: cluster:container_cpu_usage:ratio
- expr: sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name!=""}[5m]))
- / sum(machine_cpu_cores)
- - record: apiserver_latency_seconds:quantile
- expr: histogram_quantile(0.99, rate(apiserver_request_latencies_bucket[5m])) /
- 1e+06
- labels:
- quantile: "0.99"
- - record: apiserver_latency:quantile_seconds
- expr: histogram_quantile(0.9, rate(apiserver_request_latencies_bucket[5m])) /
- 1e+06
- labels:
- quantile: "0.9"
- - record: apiserver_latency_seconds:quantile
- expr: histogram_quantile(0.5, rate(apiserver_request_latencies_bucket[5m])) /
- 1e+06
- labels:
- quantile: "0.5"
- - alert: APIServerLatencyHigh
- expr: apiserver_latency_seconds:quantile{quantile="0.99",subresource!="log",verb!~"^(?:WATCH|WATCHLIST|PROXY|CONNECT)$"}
- > 1
- for: 10m
- labels:
- severity: warning
- annotations:
- description: the API server has a 99th percentile latency of {{ $value }} seconds
- for {{$labels.verb}} {{$labels.resource}}
- - alert: APIServerLatencyHigh
- expr: apiserver_latency_seconds:quantile{quantile="0.99",subresource!="log",verb!~"^(?:WATCH|WATCHLIST|PROXY|CONNECT)$"}
- > 4
- for: 10m
- labels:
- severity: critical
- annotations:
- description: the API server has a 99th percentile latency of {{ $value }} seconds
- for {{$labels.verb}} {{$labels.resource}}
- - alert: APIServerErrorsHigh
- expr: rate(apiserver_request_count{code=~"^(?:5..)$"}[5m]) / rate(apiserver_request_count[5m])
- * 100 > 2
- for: 10m
- labels:
- severity: warning
- annotations:
- description: API server returns errors for {{ $value }}% of requests
- - alert: APIServerErrorsHigh
- expr: rate(apiserver_request_count{code=~"^(?:5..)$"}[5m]) / rate(apiserver_request_count[5m])
- * 100 > 5
- for: 10m
- labels:
- severity: critical
- annotations:
- description: API server returns errors for {{ $value }}% of requests
- - alert: K8SApiserverDown
- expr: absent(up{job="apiserver"} == 1)
- for: 20m
- labels:
- severity: critical
- annotations:
- description: No API servers are reachable or all have disappeared from service
- discovery
-
- - alert: K8sCertificateExpirationNotice
- labels:
- severity: warning
- annotations:
- description: Kubernetes API Certificate is expiring soon (less than 7 days)
- expr: sum(apiserver_client_certificate_expiration_seconds_bucket{le="604800"}) > 0
-
- - alert: K8sCertificateExpirationNotice
- labels:
- severity: critical
- annotations:
- description: Kubernetes API Certificate is expiring in less than 1 day
- expr: sum(apiserver_client_certificate_expiration_seconds_bucket{le="86400"}) > 0
- node.rules.yaml: |
- groups:
- - name: node.rules
- rules:
- - record: instance:node_cpu:rate:sum
- expr: sum(rate(node_cpu{mode!="idle",mode!="iowait",mode!~"^(?:guest.*)$"}[3m]))
- BY (instance)
- - record: instance:node_filesystem_usage:sum
- expr: sum((node_filesystem_size{mountpoint="/"} - node_filesystem_free{mountpoint="/"}))
- BY (instance)
- - record: instance:node_network_receive_bytes:rate:sum
- expr: sum(rate(node_network_receive_bytes[3m])) BY (instance)
- - record: instance:node_network_transmit_bytes:rate:sum
- expr: sum(rate(node_network_transmit_bytes[3m])) BY (instance)
- - record: instance:node_cpu:ratio
- expr: sum(rate(node_cpu{mode!="idle"}[5m])) WITHOUT (cpu, mode) / ON(instance)
- GROUP_LEFT() count(sum(node_cpu) BY (instance, cpu)) BY (instance)
- - record: cluster:node_cpu:sum_rate5m
- expr: sum(rate(node_cpu{mode!="idle"}[5m]))
- - record: cluster:node_cpu:ratio
- expr: cluster:node_cpu:rate5m / count(sum(node_cpu) BY (instance, cpu))
- - alert: NodeExporterDown
- expr: absent(up{job="node-exporter"} == 1)
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Prometheus could not scrape a node-exporter for more than 10m,
- or node-exporters have disappeared from discovery
- - alert: NodeDiskRunningFull
- expr: predict_linear(node_filesystem_free[6h], 3600 * 24) < 0
- for: 30m
- labels:
- severity: warning
- annotations:
- description: device {{$labels.device}} on node {{$labels.instance}} is running
- full within the next 24 hours (mounted at {{$labels.mountpoint}})
- - alert: NodeDiskRunningFull
- expr: predict_linear(node_filesystem_free[30m], 3600 * 2) < 0
- for: 10m
- labels:
- severity: critical
- annotations:
- description: device {{$labels.device}} on node {{$labels.instance}} is running
- full within the next 2 hours (mounted at {{$labels.mountpoint}})
- - alert: InactiveRAIDDisk
- expr: node_md_disks - node_md_disks_active > 0
- for: 10m
- labels:
- severity: warning
- annotations:
- description: '{{$value}} RAID disk(s) on node {{$labels.instance}} are inactive'
- prometheus.rules.yaml: |
- groups:
- - name: prometheus.rules
- rules:
- - alert: PrometheusConfigReloadFailed
- expr: prometheus_config_last_reload_successful == 0
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Reloading Prometheus' configuration has failed for {{$labels.namespace}}/{{$labels.pod}}
- - alert: PrometheusNotificationQueueRunningFull
- expr: predict_linear(prometheus_notifications_queue_length[5m], 60 * 30) > prometheus_notifications_queue_capacity
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Prometheus' alert notification queue is running full for {{$labels.namespace}}/{{
- $labels.pod}}
- - alert: PrometheusErrorSendingAlerts
- expr: rate(prometheus_notifications_errors_total[5m]) / rate(prometheus_notifications_sent_total[5m])
- > 0.01
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Errors while sending alerts from Prometheus {{$labels.namespace}}/{{
- $labels.pod}} to Alertmanager {{$labels.Alertmanager}}
- - alert: PrometheusErrorSendingAlerts
- expr: rate(prometheus_notifications_errors_total[5m]) / rate(prometheus_notifications_sent_total[5m])
- > 0.03
- for: 10m
- labels:
- severity: critical
- annotations:
- description: Errors while sending alerts from Prometheus {{$labels.namespace}}/{{
- $labels.pod}} to Alertmanager {{$labels.Alertmanager}}
- - alert: PrometheusNotConnectedToAlertmanagers
- expr: prometheus_notifications_alertmanagers_discovered < 1
- for: 10m
- labels:
- severity: warning
- annotations:
- description: Prometheus {{ $labels.namespace }}/{{ $labels.pod}} is not connected
- to any Alertmanagers
- - alert: PrometheusTSDBReloadsFailing
- expr: increase(prometheus_tsdb_reloads_failures_total[2h]) > 0
- for: 12h
- labels:
- severity: warning
- annotations:
- description: '{{$labels.job}} at {{$labels.instance}} had {{$value | humanize}}
- reload failures over the last four hours.'
- summary: Prometheus has issues reloading data blocks from disk
- - alert: PrometheusTSDBCompactionsFailing
- expr: increase(prometheus_tsdb_compactions_failed_total[2h]) > 0
- for: 12h
- labels:
- severity: warning
- annotations:
- description: '{{$labels.job}} at {{$labels.instance}} had {{$value | humanize}}
- compaction failures over the last four hours.'
- summary: Prometheus has issues compacting sample blocks
- - alert: PrometheusTSDBWALCorruptions
- expr: tsdb_wal_corruptions_total > 0
- for: 4h
- labels:
- severity: warning
- annotations:
- description: '{{$labels.job}} at {{$labels.instance}} has a corrupted write-ahead
- log (WAL).'
- summary: Prometheus write-ahead log is corrupted
- - alert: PrometheusNotIngestingSamples
- expr: rate(prometheus_tsdb_head_samples_appended_total[5m]) <= 0
- for: 10m
- labels:
- severity: warning
- annotations:
- description: "Prometheus {{ $labels.namespace }}/{{ $labels.pod}} isn't ingesting samples."
- summary: "Prometheus isn't ingesting samples"
diff --git a/extensions/metrics-cluster-feature/resources/05-clusterrole.yml b/extensions/metrics-cluster-feature/resources/05-clusterrole.yml
deleted file mode 100644
index 9b23edc84b..0000000000
--- a/extensions/metrics-cluster-feature/resources/05-clusterrole.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: lens-prometheus
-rules:
-- apiGroups: [""]
- resources:
- - nodes
- - nodes/proxy
- - nodes/metrics
- - services
- - endpoints
- - pods
- - ingresses
- - configmaps
- verbs: ["get", "list", "watch"]
-- nonResourceURLs: ["/metrics"]
- verbs: ["get"]
diff --git a/extensions/metrics-cluster-feature/resources/06-clusterrole-binding.yml b/extensions/metrics-cluster-feature/resources/06-clusterrole-binding.yml
deleted file mode 100644
index 37fc605f05..0000000000
--- a/extensions/metrics-cluster-feature/resources/06-clusterrole-binding.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: lens-prometheus
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: lens-prometheus
-subjects:
-- kind: ServiceAccount
- name: prometheus
- namespace: lens-metrics
diff --git a/extensions/metrics-cluster-feature/resources/10-node-exporter-ds.yml.hb b/extensions/metrics-cluster-feature/resources/10-node-exporter-ds.yml.hb
deleted file mode 100644
index c02fb93321..0000000000
--- a/extensions/metrics-cluster-feature/resources/10-node-exporter-ds.yml.hb
+++ /dev/null
@@ -1,79 +0,0 @@
-{{#if nodeExporter.enabled}}
-apiVersion: apps/v1
-kind: DaemonSet
-metadata:
- name: node-exporter
- namespace: lens-metrics
-spec:
- updateStrategy:
- type: RollingUpdate
- rollingUpdate:
- maxUnavailable: 1
- selector:
- matchLabels:
- name: node-exporter
- phase: prod
- template:
- metadata:
- labels:
- name: node-exporter
- phase: prod
- annotations:
- seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
- spec:
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/os
- operator: In
- values:
- - linux
- securityContext:
- runAsNonRoot: true
- runAsUser: 65534
- hostPID: true
- containers:
- - name: node-exporter
- image: quay.io/prometheus/node-exporter:v1.1.2
- args:
- - --path.procfs=/host/proc
- - --path.sysfs=/host/sys
- - --path.rootfs=/host/root
- - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker|var/lib/containerd|var/lib/containers/.+)($|/)
- - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
- ports:
- - name: metrics
- containerPort: 9100
- resources:
- requests:
- cpu: 10m
- memory: 24Mi
- limits:
- cpu: 200m
- memory: 100Mi
- volumeMounts:
- - name: proc
- mountPath: /host/proc
- readOnly: true
- - name: sys
- mountPath: /host/sys
- readOnly: true
- - name: root
- mountPath: /host/root
- readOnly: true
- tolerations:
- - effect: NoSchedule
- operator: Exists
- volumes:
- - name: proc
- hostPath:
- path: /proc
- - name: sys
- hostPath:
- path: /sys
- - name: root
- hostPath:
- path: /
-{{/if}}
diff --git a/extensions/metrics-cluster-feature/resources/11-node-exporter-svc.yml b/extensions/metrics-cluster-feature/resources/11-node-exporter-svc.yml
deleted file mode 100644
index f85e878e06..0000000000
--- a/extensions/metrics-cluster-feature/resources/11-node-exporter-svc.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- name: node-exporter
- namespace: lens-metrics
- annotations:
- prometheus.io/scrape: 'true'
-spec:
- type: ClusterIP
- clusterIP: None
- selector:
- name: node-exporter
- phase: prod
- ports:
- - name: metrics
- protocol: TCP
- port: 80
- targetPort: 9100
diff --git a/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml b/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml
deleted file mode 100644
index 8101bcc05d..0000000000
--- a/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml
+++ /dev/null
@@ -1,128 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: lens-kube-state-metrics
-rules:
- - apiGroups:
- - ""
- resources:
- - configmaps
- - secrets
- - nodes
- - pods
- - services
- - resourcequotas
- - replicationcontrollers
- - limitranges
- - persistentvolumeclaims
- - persistentvolumes
- - namespaces
- - endpoints
- verbs:
- - list
- - watch
- - apiGroups:
- - extensions
- resources:
- - daemonsets
- - deployments
- - replicasets
- - ingresses
- verbs:
- - list
- - watch
- - apiGroups:
- - networking.k8s.io
- resources:
- - ingresses
- verbs:
- - list
- - watch
- - apiGroups:
- - apps
- resources:
- - statefulsets
- - daemonsets
- - deployments
- - replicasets
- verbs:
- - list
- - watch
- - apiGroups:
- - batch
- resources:
- - cronjobs
- - jobs
- verbs:
- - list
- - watch
- - apiGroups:
- - autoscaling
- resources:
- - horizontalpodautoscalers
- verbs:
- - list
- - watch
- - apiGroups:
- - authentication.k8s.io
- resources:
- - tokenreviews
- verbs:
- - create
- - apiGroups:
- - authorization.k8s.io
- resources:
- - subjectaccessreviews
- verbs:
- - create
- - apiGroups:
- - policy
- resources:
- - poddisruptionbudgets
- verbs:
- - list
- - watch
- - apiGroups:
- - certificates.k8s.io
- resources:
- - certificatesigningrequests
- verbs:
- - list
- - watch
- - apiGroups:
- - storage.k8s.io
- resources:
- - storageclasses
- - volumeattachments
- verbs:
- - list
- - watch
- - apiGroups:
- - admissionregistration.k8s.io
- resources:
- - mutatingwebhookconfigurations
- - validatingwebhookconfigurations
- verbs:
- - list
- - watch
- - apiGroups:
- - networking.k8s.io
- resources:
- - networkpolicies
- verbs:
- - list
- - watch
- - apiGroups:
- - coordination.k8s.io
- resources:
- - leases
- verbs:
- - list
- - watch
- - apiGroups:
- - scheduling.k8s.io
- resources:
- - priorityclasses
- verbs:
- - list
- - watch
diff --git a/extensions/metrics-cluster-feature/resources/12.kube-state-metrics-sa.yml b/extensions/metrics-cluster-feature/resources/12.kube-state-metrics-sa.yml
deleted file mode 100644
index 2b3c22f8da..0000000000
--- a/extensions/metrics-cluster-feature/resources/12.kube-state-metrics-sa.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
- name: kube-state-metrics
- namespace: lens-metrics
diff --git a/extensions/metrics-cluster-feature/resources/13-kube-state-metrics-clusterrole-binding.yml b/extensions/metrics-cluster-feature/resources/13-kube-state-metrics-clusterrole-binding.yml
deleted file mode 100644
index e60dedae19..0000000000
--- a/extensions/metrics-cluster-feature/resources/13-kube-state-metrics-clusterrole-binding.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: lens-kube-state-metrics
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: lens-kube-state-metrics
-subjects:
-- kind: ServiceAccount
- name: kube-state-metrics
- namespace: lens-metrics
diff --git a/extensions/metrics-cluster-feature/resources/14-kube-state-metrics-deployment.yml.hb b/extensions/metrics-cluster-feature/resources/14-kube-state-metrics-deployment.yml.hb
deleted file mode 100644
index 0174d5c8f4..0000000000
--- a/extensions/metrics-cluster-feature/resources/14-kube-state-metrics-deployment.yml.hb
+++ /dev/null
@@ -1,46 +0,0 @@
-{{#if kubeStateMetrics.enabled}}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: kube-state-metrics
- namespace: lens-metrics
-spec:
- selector:
- matchLabels:
- name: kube-state-metrics
- replicas: 1
- template:
- metadata:
- labels:
- name: kube-state-metrics
- spec:
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/os
- operator: In
- values:
- - linux
- serviceAccountName: kube-state-metrics
- containers:
- - name: kube-state-metrics
- image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.0.0
- ports:
- - name: metrics
- containerPort: 8080
- readinessProbe:
- httpGet:
- path: /healthz
- port: 8080
- initialDelaySeconds: 5
- timeoutSeconds: 5
- resources:
- requests:
- cpu: 10m
- memory: 32Mi
- limits:
- cpu: 200m
- memory: 150Mi
-{{/if}}
diff --git a/extensions/metrics-cluster-feature/resources/14-kube-state-metrics-svc.yml b/extensions/metrics-cluster-feature/resources/14-kube-state-metrics-svc.yml
deleted file mode 100644
index be6168ab7a..0000000000
--- a/extensions/metrics-cluster-feature/resources/14-kube-state-metrics-svc.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- name: kube-state-metrics
- namespace: lens-metrics
- labels:
- name: kube-state-metrics
- annotations:
- prometheus.io/scrape: 'true'
-spec:
- ports:
- - name: metrics
- port: 8080
- targetPort: 8080
- protocol: TCP
- selector:
- name: kube-state-metrics
diff --git a/extensions/metrics-cluster-feature/src/metrics-feature.ts b/extensions/metrics-cluster-feature/src/metrics-feature.ts
deleted file mode 100644
index c9d4275abb..0000000000
--- a/extensions/metrics-cluster-feature/src/metrics-feature.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { Common } from "@k8slens/extensions";
-import { Renderer } from "@k8slens/extensions";
-import semver from "semver";
-import * as path from "path";
-
-const { ResourceStack, forCluster, StorageClass, Namespace } = Renderer.K8sApi;
-
-type ResourceStack = Renderer.K8sApi.ResourceStack;
-
-export interface MetricsConfiguration {
- // Placeholder for Metrics config structure
- prometheus: {
- enabled: boolean;
- };
- persistence: {
- enabled: boolean;
- storageClass: string;
- size: string;
- };
- nodeExporter: {
- enabled: boolean;
- };
- kubeStateMetrics: {
- enabled: boolean;
- };
- retention: {
- time: string;
- size: string;
- };
- alertManagers: string[];
- replicas: number;
- storageClass: string;
- version?: string;
-}
-
-export interface MetricsStatus {
- installed: boolean;
- canUpgrade: boolean;
-}
-
-export class MetricsFeature {
- name = "lens-metrics";
- latestVersion = "v2.26.0-lens1";
-
- protected stack: ResourceStack;
-
- constructor(protected cluster: Common.Catalog.KubernetesCluster) {
- this.stack = new ResourceStack(cluster, this.name);
- }
-
- get resourceFolder() {
- return path.join(__dirname, "../resources/");
- }
-
- async install(config: MetricsConfiguration): Promise {
- // Check if there are storageclasses
- const storageClassApi = forCluster(this.cluster, StorageClass);
- const scs = await storageClassApi.list();
-
- config.persistence.enabled = scs.some(sc => (
- sc.metadata?.annotations?.["storageclass.kubernetes.io/is-default-class"] === "true" ||
- sc.metadata?.annotations?.["storageclass.beta.kubernetes.io/is-default-class"] === "true"
- ));
-
- config.version = this.latestVersion;
-
- return this.stack.kubectlApplyFolder(this.resourceFolder, config, ["--prune"]);
- }
-
- async upgrade(config: MetricsConfiguration): Promise {
- return this.install(config);
- }
-
- async getStatus(): Promise {
- const status: MetricsStatus = { installed: false, canUpgrade: false };
-
- try {
- const namespaceApi = forCluster(this.cluster, Namespace);
- const namespace = await namespaceApi.get({ name: "lens-metrics" });
-
- if (namespace?.kind) {
- const currentVersion = namespace.metadata.annotations?.extensionVersion || "0.0.0";
-
- status.installed = true;
- status.canUpgrade = semver.lt(currentVersion, this.latestVersion, true);
- } else {
- status.installed = false;
- }
- } catch(e) {
- if (e?.error?.code === 404) {
- status.installed = false;
- } else {
- console.warn("[LENS-METRICS] failed to resolve install state", e);
- }
- }
-
- return status;
- }
-
- async uninstall(config: MetricsConfiguration): Promise {
- return this.stack.kubectlDeleteFolder(this.resourceFolder, config);
- }
-}
diff --git a/extensions/metrics-cluster-feature/src/metrics-settings.tsx b/extensions/metrics-cluster-feature/src/metrics-settings.tsx
deleted file mode 100644
index 4ae4235780..0000000000
--- a/extensions/metrics-cluster-feature/src/metrics-settings.tsx
+++ /dev/null
@@ -1,276 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import React from "react";
-import type { Common } from "@k8slens/extensions";
-import { Renderer } from "@k8slens/extensions";
-import { observer } from "mobx-react";
-import { computed, observable, makeObservable } from "mobx";
-import type { MetricsConfiguration } from "./metrics-feature";
-import { MetricsFeature } from "./metrics-feature";
-
-const {
- K8sApi: {
- forCluster, StatefulSet, DaemonSet, Deployment,
- },
- Component: {
- SubTitle, Switch, Button,
- },
-} = Renderer;
-
-export interface MetricsSettingsProps {
- cluster: Common.Catalog.KubernetesCluster;
-}
-
-@observer
-export class MetricsSettings extends React.Component {
- constructor(props: MetricsSettingsProps) {
- super(props);
- makeObservable(this);
- }
-
- @observable featureStates = {
- prometheus: false,
- kubeStateMetrics: false,
- nodeExporter: false,
- };
- @observable canUpgrade = false;
- @observable upgrading = false;
- @observable changed = false;
- @observable inProgress = false;
-
- config: MetricsConfiguration = {
- prometheus: {
- enabled: false,
- },
- persistence: {
- enabled: false,
- storageClass: null,
- size: "20Gi", // kubernetes yaml value (no B suffix)
- },
- nodeExporter: {
- enabled: false,
- },
- retention: {
- time: "2d",
- size: "5GiB", // argument for prometheus (requires B suffix)
- },
- kubeStateMetrics: {
- enabled: false,
- },
- alertManagers: null,
- replicas: 1,
- storageClass: null,
- };
- feature: MetricsFeature;
-
- @computed get isTogglable() {
- if (this.inProgress) return false;
- if (this.props.cluster.status.phase !== "connected") return false;
- if (this.canUpgrade) return false;
- if (!this.isActiveMetricsProvider) return false;
-
- return true;
- }
-
- get metricsProvider() {
- return this.props.cluster.spec?.metrics?.prometheus?.type || "";
- }
-
- get isActiveMetricsProvider() {
- return (!this.metricsProvider || this.metricsProvider === "lens");
- }
-
- async componentDidMount() {
- this.feature = new MetricsFeature(this.props.cluster);
-
- await this.updateFeatureStates();
- }
-
- async updateFeatureStates() {
- const status = await this.feature.getStatus();
-
- this.canUpgrade = status.canUpgrade;
-
- if (this.canUpgrade) {
- this.changed = true;
- }
-
- const statefulSet = forCluster(this.props.cluster, StatefulSet);
-
- try {
- await statefulSet.get({ name: "prometheus", namespace: "lens-metrics" });
- this.featureStates.prometheus = true;
- } catch(e) {
- if (e?.error?.code === 404) {
- this.featureStates.prometheus = false;
- } else {
- this.featureStates.prometheus = undefined;
- }
- }
-
- const deployment = forCluster(this.props.cluster, Deployment);
-
- try {
- await deployment.get({ name: "kube-state-metrics", namespace: "lens-metrics" });
- this.featureStates.kubeStateMetrics = true;
- } catch(e) {
- if (e?.error?.code === 404) {
- this.featureStates.kubeStateMetrics = false;
- } else {
- this.featureStates.kubeStateMetrics = undefined;
- }
- }
-
- const daemonSet = forCluster(this.props.cluster, DaemonSet);
-
- try {
- await daemonSet.get({ name: "node-exporter", namespace: "lens-metrics" });
- this.featureStates.nodeExporter = true;
- } catch(e) {
- if (e?.error?.code === 404) {
- this.featureStates.nodeExporter = false;
- } else {
- this.featureStates.nodeExporter = undefined;
- }
- }
- }
-
- async save() {
- this.config.prometheus.enabled = !!this.featureStates.prometheus;
- this.config.kubeStateMetrics.enabled = !!this.featureStates.kubeStateMetrics;
- this.config.nodeExporter.enabled = !!this.featureStates.nodeExporter;
-
- this.inProgress = true;
-
- try {
- if (!this.config.prometheus.enabled && !this.config.kubeStateMetrics.enabled && !this.config.nodeExporter.enabled) {
- await this.feature.uninstall(this.config);
- } else {
- await this.feature.install(this.config);
- }
- } finally {
- this.inProgress = false;
- this.changed = false;
-
- await this.updateFeatureStates();
- }
- }
-
- async togglePrometheus(enabled: boolean) {
- this.featureStates.prometheus = enabled;
- this.changed = true;
- }
-
- async toggleKubeStateMetrics(enabled: boolean) {
- this.featureStates.kubeStateMetrics = enabled;
- this.changed = true;
- }
-
- async toggleNodeExporter(enabled: boolean) {
- this.featureStates.nodeExporter = enabled;
- this.changed = true;
- }
-
- @computed get buttonLabel() {
- const allDisabled = !this.featureStates.kubeStateMetrics && !this.featureStates.nodeExporter && !this.featureStates.prometheus;
-
- if (this.inProgress && this.canUpgrade) return "Upgrading ...";
- if (this.inProgress && allDisabled) return "Uninstalling ...";
- if (this.inProgress) return "Applying ...";
- if (this.canUpgrade) return "Upgrade";
-
- if (this.changed && allDisabled) {
- return "Uninstall";
- }
-
- return "Apply";
- }
-
- render() {
- return (
-
- { this.props.cluster.status.phase !== "connected" && (
-
-
- Lens Metrics settings requires established connection to the cluster.
-
-
- )}
- { !this.isActiveMetricsProvider && (
-
-
- Other metrics provider is currently active. See "Metrics" tab for details.
-
-
- )}
-
-
- this.togglePrometheus(checked)}
- name="prometheus"
- >
- Enable bundled Prometheus metrics stack
-
-
- Enable timeseries data visualization (Prometheus stack) for your cluster.
-
-
-
-
-
- this.toggleKubeStateMetrics(checked)}
- name="kube-state-metrics"
- >
- Enable bundled kube-state-metrics stack
-
-
- Enable Kubernetes API object metrics for your cluster.
- Enable this only if you don't have existing kube-state-metrics stack installed.
-
-
-
-
-
- this.toggleNodeExporter(checked)}
- name="node-exporter"
- >
- Enable bundled node-exporter stack
-
-
- Enable node level metrics for your cluster.
- Enable this only if you don't have existing node-exporter stack installed.
-
-
-
-
-
- this.save()}
- disabled={!this.changed}
- style={{ width: "20ch", padding: "0.5rem" }}
- />
-
- {this.canUpgrade && (
-
- An update is available for enabled metrics components.
-
- )}
-
-
-
- );
- }
-}
diff --git a/extensions/metrics-cluster-feature/tsconfig.json b/extensions/metrics-cluster-feature/tsconfig.json
deleted file mode 100644
index f60a98c9ad..0000000000
--- a/extensions/metrics-cluster-feature/tsconfig.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "outDir": "dist",
- "module": "CommonJS",
- "target": "ES2017",
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
- "moduleResolution": "Node",
- "sourceMap": false,
- "declaration": false,
- "strict": false,
- "noImplicitAny": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "experimentalDecorators": true,
- "useDefineForClassFields": true,
- "jsx": "react"
- },
- "include": [
- "./**/*.ts",
- "./**/*.tsx"
- ],
- "exclude": [
- "node_modules",
- "*.js"
- ]
-}
diff --git a/extensions/metrics-cluster-feature/webpack.config.js b/extensions/metrics-cluster-feature/webpack.config.js
deleted file mode 100644
index 951cffdabc..0000000000
--- a/extensions/metrics-cluster-feature/webpack.config.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-const path = require("path");
-
-module.exports = [
- {
- entry: "./renderer.tsx",
- context: __dirname,
- target: "electron-renderer",
- mode: "production",
- optimization: {
- minimize: false,
- },
- module: {
- rules: [
- {
- test: /\.tsx?$/,
- use: "ts-loader",
- exclude: /node_modules/,
- },
- ],
- },
- externals: [
- {
- "@k8slens/extensions": "var global.LensExtensions",
- "react": "var global.React",
- "react-dom": "var global.ReactDOM",
- "mobx": "var global.Mobx",
- "mobx-react": "var global.MobxReact",
- },
- ],
- resolve: {
- extensions: [ ".tsx", ".ts", ".js" ],
- },
- output: {
- libraryTarget: "commonjs2",
- globalObject: "this",
- filename: "renderer.js",
- path: path.resolve(__dirname, "dist"),
- },
- node: {
- __dirname: false,
- },
- },
-];
diff --git a/extensions/node-menu/package.json b/extensions/node-menu/package.json
deleted file mode 100644
index df2d490ee0..0000000000
--- a/extensions/node-menu/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "lens-node-menu",
- "version": "6.1.0",
- "description": "Lens node menu",
- "renderer": "dist/renderer.js",
- "lens": {
- "metadata": {},
- "styles": []
- },
- "scripts": {
- "build": "npx webpack",
- "dev": "npx webpack -- --watch",
- "test": "npx jest --passWithNoTests --env=jsdom src $@"
- },
- "files": [
- "dist/**/*"
- ],
- "dependencies": {},
- "devDependencies": {
- "@k8slens/extensions": "file:../../src/extensions/npm/extensions"
- }
-}
diff --git a/extensions/node-menu/renderer.tsx b/extensions/node-menu/renderer.tsx
deleted file mode 100644
index 31557c908d..0000000000
--- a/extensions/node-menu/renderer.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { Renderer } from "@k8slens/extensions";
-import React from "react";
-import type { NodeMenuProps } from "./src/node-menu";
-import { NodeMenu } from "./src/node-menu";
-
-export default class NodeMenuRendererExtension extends Renderer.LensExtension {
- kubeObjectMenuItems = [
- {
- kind: "Node",
- apiVersions: ["v1"],
- components: {
- MenuItem: (props: NodeMenuProps) => ,
- },
- },
- ];
-}
diff --git a/extensions/node-menu/src/node-menu.tsx b/extensions/node-menu/src/node-menu.tsx
deleted file mode 100644
index adc7576206..0000000000
--- a/extensions/node-menu/src/node-menu.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import React from "react";
-import { Common, Renderer } from "@k8slens/extensions";
-
-type Node = Renderer.K8sApi.Node;
-
-const {
- Component: {
- terminalStore,
- createTerminalTab,
- ConfirmDialog,
- MenuItem,
- Icon,
- },
- Navigation,
-} = Renderer;
-const {
- App,
-} = Common;
-
-
-export interface NodeMenuProps extends Renderer.Component.KubeObjectMenuProps {
-}
-
-export function NodeMenu(props: NodeMenuProps) {
- const { object: node, toolbar } = props;
-
- if (!node) {
- return null;
- }
-
- const nodeName = node.getName();
- const kubectlPath = App.Preferences.getKubectlPath() || "kubectl";
-
- const sendToTerminal = (command: string) => {
- terminalStore.sendCommand(command, {
- enter: true,
- newTab: true,
- });
- Navigation.hideDetails();
- };
-
- const shell = () => {
- createTerminalTab({
- title: `Node: ${nodeName}`,
- node: nodeName,
- });
- Navigation.hideDetails();
- };
-
- const cordon = () => {
- sendToTerminal(`${kubectlPath} cordon ${nodeName}`);
- };
-
- const unCordon = () => {
- sendToTerminal(`${kubectlPath} uncordon ${nodeName}`);
- };
-
- const drain = () => {
- const command = `${kubectlPath} drain ${nodeName} --delete-local-data --ignore-daemonsets --force`;
-
- ConfirmDialog.open({
- ok: () => sendToTerminal(command),
- labelOk: `Drain Node`,
- message: (
-
- {"Are you sure you want to drain "}
- {nodeName}
- ?
-
- ),
- });
- };
-
- return (
- <>
-
-
- Shell
-
- {
- node.isUnschedulable()
- ? (
-
-
- Uncordon
-
- )
- : (
-
-
- Cordon
-
- )
- }
-
-
- Drain
-
- >
- );
-}
diff --git a/extensions/node-menu/tsconfig.json b/extensions/node-menu/tsconfig.json
deleted file mode 100644
index 5b6c61577e..0000000000
--- a/extensions/node-menu/tsconfig.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "outDir": "dist",
- "module": "CommonJS",
- "target": "ES2017",
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
- "moduleResolution": "Node",
- "sourceMap": false,
- "declaration": false,
- "strict": false,
- "noImplicitAny": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "experimentalDecorators": true,
- "useDefineForClassFields": true,
- "jsx": "react"
- },
- "include": [
- "./*.ts",
- "./*.tsx"
- ],
- "exclude": [
- "node_modules",
- "*.js"
- ]
-}
diff --git a/extensions/node-menu/webpack.config.js b/extensions/node-menu/webpack.config.js
deleted file mode 100644
index 8fe82e48a7..0000000000
--- a/extensions/node-menu/webpack.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-const path = require("path");
-
-module.exports = [
- {
- entry: "./renderer.tsx",
- context: __dirname,
- target: "electron-renderer",
- mode: "production",
- optimization: {
- minimize: false,
- },
- module: {
- rules: [
- {
- test: /\.tsx?$/,
- use: "ts-loader",
- exclude: /node_modules/,
- },
- ],
- },
- externals: [
- {
- "@k8slens/extensions": "var global.LensExtensions",
- "react": "var global.React",
- "react-dom": "var global.ReactDOM",
- "mobx": "var global.Mobx",
- "mobx-react": "var global.MobxReact",
- },
- ],
- resolve: {
- extensions: [ ".tsx", ".ts", ".js" ],
- },
- output: {
- libraryTarget: "commonjs2",
- globalObject: "this",
- filename: "renderer.js",
- path: path.resolve(__dirname, "dist"),
- },
- },
-];
diff --git a/extensions/pod-menu/package.json b/extensions/pod-menu/package.json
deleted file mode 100644
index 3b44d3c44d..0000000000
--- a/extensions/pod-menu/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "lens-pod-menu",
- "version": "6.1.0",
- "description": "Lens pod menu",
- "renderer": "dist/renderer.js",
- "lens": {
- "metadata": {},
- "styles": []
- },
- "scripts": {
- "build": "npx webpack",
- "dev": "npx webpack -- --watch",
- "test": "npx jest --passWithNoTests --env=jsdom src $@"
- },
- "files": [
- "dist/**/*"
- ],
- "dependencies": {},
- "devDependencies": {
- "@k8slens/extensions": "file:../../src/extensions/npm/extensions"
- }
-}
diff --git a/extensions/pod-menu/renderer.tsx b/extensions/pod-menu/renderer.tsx
deleted file mode 100644
index 4788da9707..0000000000
--- a/extensions/pod-menu/renderer.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { Renderer } from "@k8slens/extensions";
-import type { PodAttachMenuProps } from "./src/attach-menu";
-import { PodAttachMenu } from "./src/attach-menu";
-import type { PodShellMenuProps } from "./src/shell-menu";
-import { PodShellMenu } from "./src/shell-menu";
-import type { PodLogsMenuProps } from "./src/logs-menu";
-import { PodLogsMenu } from "./src/logs-menu";
-import React from "react";
-
-export default class PodMenuRendererExtension extends Renderer.LensExtension {
- kubeObjectMenuItems = [
- {
- kind: "Pod",
- apiVersions: ["v1"],
- components: {
- MenuItem: (props: PodAttachMenuProps) => ,
- },
- },
- {
- kind: "Pod",
- apiVersions: ["v1"],
- components: {
- MenuItem: (props: PodShellMenuProps) => ,
- },
- },
- {
- kind: "Pod",
- apiVersions: ["v1"],
- components: {
- MenuItem: (props: PodLogsMenuProps) => ,
- },
- },
- ];
-}
diff --git a/extensions/pod-menu/src/attach-menu.tsx b/extensions/pod-menu/src/attach-menu.tsx
deleted file mode 100644
index fd6250bd30..0000000000
--- a/extensions/pod-menu/src/attach-menu.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-
-
-import React from "react";
-import { Renderer, Common } from "@k8slens/extensions";
-
-type Pod = Renderer.K8sApi.Pod;
-
-const {
- Component: {
- createTerminalTab,
- terminalStore,
- MenuItem,
- Icon,
- SubMenu,
- StatusBrick,
- },
- Navigation,
-} = Renderer;
-const {
- Util,
- App,
-} = Common;
-
-export interface PodAttachMenuProps extends Renderer.Component.KubeObjectMenuProps {
-}
-
-export class PodAttachMenu extends React.Component {
- async attachToPod(container?: string) {
- const { object: pod } = this.props;
-
- const kubectlPath = App.Preferences.getKubectlPath() || "kubectl";
- const commandParts = [
- kubectlPath,
- "attach",
- "-i",
- "-t",
- "-n", pod.getNs(),
- pod.getName(),
- ];
-
- if (window.navigator.platform !== "Win32") {
- commandParts.unshift("exec");
- }
-
- if (container) {
- commandParts.push("-c", container);
- }
-
- const shell = createTerminalTab({
- title: `Pod: ${pod.getName()} (namespace: ${pod.getNs()}) [Attached]`,
- });
-
- terminalStore.sendCommand(commandParts.join(" "), {
- enter: true,
- tabId: shell.id,
- });
-
- Navigation.hideDetails();
- }
-
- render() {
- const { object, toolbar } = this.props;
- const containers = object.getRunningContainers();
-
- if (!containers.length) return null;
-
- return (
- this.attachToPod(containers[0].name))}>
-
- Attach Pod
- {containers.length > 1 && (
- <>
-
-
- {
- containers.map(container => {
- const { name } = container;
-
- return (
- this.attachToPod(name))}
- className="flex align-center"
- >
-
- {name}
-
- );
- })
- }
-
- >
- )}
-
- );
- }
-}
diff --git a/extensions/pod-menu/src/logs-menu.tsx b/extensions/pod-menu/src/logs-menu.tsx
deleted file mode 100644
index e5c8496d3d..0000000000
--- a/extensions/pod-menu/src/logs-menu.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import React from "react";
-import { Renderer, Common } from "@k8slens/extensions";
-
-type Pod = Renderer.K8sApi.Pod;
-type IPodContainer = Renderer.K8sApi.IPodContainer;
-
-const {
- Component: {
- logTabStore,
- MenuItem,
- Icon,
- SubMenu,
- StatusBrick,
- },
- Navigation,
-} = Renderer;
-const {
- Util,
-} = Common;
-
-export interface PodLogsMenuProps extends Renderer.Component.KubeObjectMenuProps {
-}
-
-export class PodLogsMenu extends React.Component {
- showLogs(container: IPodContainer) {
- Navigation.hideDetails();
- const pod = this.props.object;
-
- logTabStore.createPodTab({
- selectedPod: pod,
- selectedContainer: container,
- });
- }
-
- render() {
- const { object: pod, toolbar } = this.props;
- const containers = pod.getAllContainers();
- const statuses = pod.getContainerStatuses();
-
- if (!containers.length) return null;
-
- return (
- this.showLogs(containers[0]))}>
-
- Logs
- {containers.length > 1 && (
- <>
-
-
- {
- containers.map(container => {
- const { name } = container;
- const status = statuses.find(status => status.name === name);
- const brick = status ? (
-
- ) : null;
-
- return (
- this.showLogs(container))}
- className="flex align-center"
- >
- {brick}
- {name}
-
- );
- })
- }
-
- >
- )}
-
- );
- }
-}
diff --git a/extensions/pod-menu/src/shell-menu.tsx b/extensions/pod-menu/src/shell-menu.tsx
deleted file mode 100644
index 36be7c470b..0000000000
--- a/extensions/pod-menu/src/shell-menu.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-
-
-import React from "react";
-import { Renderer, Common } from "@k8slens/extensions";
-
-type Pod = Renderer.K8sApi.Pod;
-
-const {
- Component: {
- createTerminalTab,
- terminalStore,
- MenuItem,
- Icon,
- SubMenu,
- StatusBrick,
- },
- Navigation,
-} = Renderer;
-const {
- Util,
- App,
-} = Common;
-
-export interface PodShellMenuProps extends Renderer.Component.KubeObjectMenuProps {
-}
-
-export class PodShellMenu extends React.Component {
- async execShell(container?: string) {
- const { object: pod } = this.props;
-
- const kubectlPath = App.Preferences.getKubectlPath() || "kubectl";
- const commandParts = [
- kubectlPath,
- "exec",
- "-i",
- "-t",
- "-n", pod.getNs(),
- pod.getName(),
- ];
-
- if (window.navigator.platform !== "Win32") {
- commandParts.unshift("exec");
- }
-
- if (container) {
- commandParts.push("-c", container);
- }
-
- commandParts.push("--");
-
- if (pod.getSelectedNodeOs() === "windows") {
- commandParts.push("powershell");
- } else {
- commandParts.push('sh -c "clear; (bash || ash || sh)"');
- }
-
- const shell = createTerminalTab({
- title: `Pod: ${pod.getName()} (namespace: ${pod.getNs()})`,
- });
-
- terminalStore.sendCommand(commandParts.join(" "), {
- enter: true,
- tabId: shell.id,
- });
-
- Navigation.hideDetails();
- }
-
- render() {
- const { object, toolbar } = this.props;
- const containers = object.getRunningContainers();
-
- if (!containers.length) return null;
-
- return (
- this.execShell(containers[0].name))}>
-
- Shell
- {containers.length > 1 && (
- <>
-
-
- {
- containers.map(container => {
- const { name } = container;
-
- return (
- this.execShell(name))}
- className="flex align-center"
- >
-
- {name}
-
- );
- })
- }
-
- >
- )}
-
- );
- }
-}
diff --git a/extensions/pod-menu/tsconfig.json b/extensions/pod-menu/tsconfig.json
deleted file mode 100644
index 5b6c61577e..0000000000
--- a/extensions/pod-menu/tsconfig.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "outDir": "dist",
- "module": "CommonJS",
- "target": "ES2017",
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
- "moduleResolution": "Node",
- "sourceMap": false,
- "declaration": false,
- "strict": false,
- "noImplicitAny": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "experimentalDecorators": true,
- "useDefineForClassFields": true,
- "jsx": "react"
- },
- "include": [
- "./*.ts",
- "./*.tsx"
- ],
- "exclude": [
- "node_modules",
- "*.js"
- ]
-}
diff --git a/extensions/pod-menu/webpack.config.js b/extensions/pod-menu/webpack.config.js
deleted file mode 100644
index 8fe82e48a7..0000000000
--- a/extensions/pod-menu/webpack.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-const path = require("path");
-
-module.exports = [
- {
- entry: "./renderer.tsx",
- context: __dirname,
- target: "electron-renderer",
- mode: "production",
- optimization: {
- minimize: false,
- },
- module: {
- rules: [
- {
- test: /\.tsx?$/,
- use: "ts-loader",
- exclude: /node_modules/,
- },
- ],
- },
- externals: [
- {
- "@k8slens/extensions": "var global.LensExtensions",
- "react": "var global.React",
- "react-dom": "var global.ReactDOM",
- "mobx": "var global.Mobx",
- "mobx-react": "var global.MobxReact",
- },
- ],
- resolve: {
- extensions: [ ".tsx", ".ts", ".js" ],
- },
- output: {
- libraryTarget: "commonjs2",
- globalObject: "this",
- filename: "renderer.js",
- path: path.resolve(__dirname, "dist"),
- },
- },
-];
diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts
index 842a38da88..e0b865d5af 100644
--- a/integration/__tests__/cluster-pages.tests.ts
+++ b/integration/__tests__/cluster-pages.tests.ts
@@ -76,60 +76,6 @@ describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
10 * 60 * 1000,
);
- it(
- "show logs and highlight the log search entries",
- async () => {
- await navigateToPods(frame);
-
- const namespacesSelector = await frame.waitForSelector(
- ".NamespaceSelect",
- );
-
- await namespacesSelector.click();
- await namespacesSelector.type("kube-system");
- await namespacesSelector.press("Enter");
- await namespacesSelector.click();
-
- const kubeApiServerRow = await frame.waitForSelector(
- "div.TableCell >> text=kube-apiserver",
- );
-
- await kubeApiServerRow.click();
- await frame.waitForSelector(".Drawer", { state: "visible" });
-
- const showPodLogsIcon = await frame.waitForSelector(
- ".Drawer .drawer-title .Icon >> text=subject",
- );
-
- showPodLogsIcon.click();
-
- // Check if controls are available
- await frame.waitForSelector(".Dock.isOpen");
- await frame.waitForSelector(".LogList .VirtualList");
- await frame.waitForSelector(".LogResourceSelector");
-
- const logSearchInput = await frame.waitForSelector(
- ".LogSearch .SearchInput input",
- );
-
- await logSearchInput.type(":");
- await frame.waitForSelector(".LogList .list span.active");
-
- const showTimestampsButton = await frame.waitForSelector(
- "[data-testid='log-controls'] .show-timestamps",
- );
-
- await showTimestampsButton.click();
-
- const showPreviousButton = await frame.waitForSelector(
- "[data-testid='log-controls'] .show-previous",
- );
-
- await showPreviousButton.click();
- },
- 10 * 60 * 1000,
- );
-
it(
"should show the default namespaces",
async () => {
diff --git a/package.json b/package.json
index ac5a76103c..c9b037d6a8 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,12 @@
"bundledHelmVersion": "3.7.2",
"sentryDsn": "",
"contentSecurityPolicy": "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:",
+<<<<<<< HEAD
"welcomeRoute": "/welcome"
+=======
+ "welcomeRoute": "/welcome",
+ "extensions": []
+>>>>>>> master
},
"engines": {
"node": ">=16 <17"
@@ -233,7 +238,7 @@
"@astronautlabs/jsonpath": "^1.1.0",
"@hapi/call": "^9.0.0",
"@hapi/subtext": "^7.0.4",
- "@kubernetes/client-node": "^0.17.1",
+ "@kubernetes/client-node": "^0.18.0",
"@material-ui/styles": "^4.11.5",
"@ogre-tools/fp": "^12.0.1",
"@ogre-tools/injectable": "^12.0.1",
@@ -256,7 +261,7 @@
"filehound": "^1.17.6",
"fs-extra": "^9.0.1",
"glob-to-regexp": "^0.4.1",
- "got": "^11.8.5",
+ "got": "^11.8.6",
"grapheme-splitter": "^1.0.4",
"handlebars": "^4.7.7",
"history": "^4.10.1",
@@ -275,7 +280,7 @@
"mobx-utils": "^6.0.4",
"mock-fs": "^5.2.0",
"moment": "^2.29.4",
- "moment-timezone": "^0.5.39",
+ "moment-timezone": "^0.5.40",
"node-fetch": "^3.3.0",
"node-pty": "0.10.1",
"npm": "^8.19.3",
@@ -290,7 +295,7 @@
"readable-stream": "^3.6.0",
"request": "^2.88.2",
"request-promise-native": "^1.0.9",
- "rfc6902": "^5.0.0",
+ "rfc6902": "^5.0.1",
"selfsigned": "^2.1.1",
"semver": "^7.3.8",
"tar": "^6.1.13",
@@ -344,7 +349,7 @@
"@types/memorystream": "^0.3.0",
"@types/mini-css-extract-plugin": "^2.4.0",
"@types/mock-fs": "^4.13.1",
- "@types/node": "^16.18.9",
+ "@types/node": "^16.18.10",
"@types/proper-lockfile": "^4.1.2",
"@types/randomcolor": "^0.5.7",
"@types/react": "^17.0.45",
@@ -388,9 +393,9 @@
"electron": "^19.1.9",
"electron-builder": "^23.6.0",
"electron-notarize": "^0.3.0",
- "esbuild": "^0.16.7",
+ "esbuild": "^0.16.10",
"esbuild-loader": "^2.20.0",
- "eslint": "^8.29.0",
+ "eslint": "^8.30.0",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.26.0",
@@ -416,7 +421,7 @@
"node-gyp": "^8.3.0",
"node-loader": "^2.0.0",
"nodemon": "^2.0.20",
- "playwright": "^1.28.1",
+ "playwright": "^1.29.0",
"postcss": "^8.4.20",
"postcss-loader": "^6.2.1",
"query-string": "^7.1.3",
@@ -429,7 +434,7 @@
"react-select-event": "^5.5.1",
"react-table": "^7.8.0",
"react-window": "^1.8.8",
- "sass": "^1.56.1",
+ "sass": "^1.57.1",
"sass-loader": "^12.6.0",
"sharp": "^0.31.2",
"style-loader": "^3.3.1",
@@ -439,7 +444,7 @@
"ts-node": "^10.9.1",
"type-fest": "^2.14.0",
"typed-emitter": "^1.4.0",
- "typedoc": "0.23.22",
+ "typedoc": "0.23.23",
"typedoc-plugin-markdown": "^3.13.6",
"typescript": "^4.9.4",
"typescript-plugin-css-modules": "^3.4.0",
diff --git a/src/common/cluster-store/allowed-resources-injection-token.ts b/src/common/cluster-store/allowed-resources-injection-token.ts
index 353d0b309c..5b71038d04 100644
--- a/src/common/cluster-store/allowed-resources-injection-token.ts
+++ b/src/common/cluster-store/allowed-resources-injection-token.ts
@@ -5,7 +5,8 @@
import { getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx";
+import type { KubeApiResourceDescriptor } from "../rbac";
-export const allowedResourcesInjectionToken = getInjectionToken>>({
- id: "allowed-resources",
+export const shouldShowResourceInjectionToken = getInjectionToken, KubeApiResourceDescriptor>({
+ id: "should-show-resource",
});
diff --git a/src/common/cluster/authorization-namespace-review.injectable.ts b/src/common/cluster/authorization-namespace-review.injectable.ts
deleted file mode 100644
index aa78453569..0000000000
--- a/src/common/cluster/authorization-namespace-review.injectable.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { KubeConfig } from "@kubernetes/client-node";
-import { AuthorizationV1Api } from "@kubernetes/client-node";
-import { getInjectable } from "@ogre-tools/injectable";
-import type { Logger } from "../logger";
-import loggerInjectable from "../logger.injectable";
-import type { KubeApiResource } from "../rbac";
-
-/**
- * Requests the permissions for actions on the kube cluster
- * @param namespace The namespace of the resources
- * @param availableResources List of available resources in the cluster to resolve glob values fir api groups
- * @returns list of allowed resources names
- */
-export type RequestNamespaceResources = (namespace: string, availableResources: KubeApiResource[]) => Promise;
-
-/**
- * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
- */
-export type AuthorizationNamespaceReview = (proxyConfig: KubeConfig) => RequestNamespaceResources;
-
-interface Dependencies {
- logger: Logger;
-}
-
-const authorizationNamespaceReview = ({ logger }: Dependencies): AuthorizationNamespaceReview => {
- return (proxyConfig) => {
-
- const api = proxyConfig.makeApiClient(AuthorizationV1Api);
-
- return async (namespace, availableResources) => {
- try {
- const { body } = await api.createSelfSubjectRulesReview({
- apiVersion: "authorization.k8s.io/v1",
- kind: "SelfSubjectRulesReview",
- spec: { namespace },
- });
-
- const resources = new Set();
-
- body.status?.resourceRules.forEach(resourceRule => {
- if (!resourceRule.verbs.some(verb => ["*", "list"].includes(verb)) || !resourceRule.resources) {
- return;
- }
-
- const apiGroups = resourceRule.apiGroups;
-
- if (resourceRule.resources.length === 1 && resourceRule.resources[0] === "*" && apiGroups) {
- if (apiGroups[0] === "*") {
- availableResources.forEach(resource => resources.add(resource.apiName));
- } else {
- availableResources.forEach((apiResource)=> {
- if (apiGroups.includes(apiResource.group || "")) {
- resources.add(apiResource.apiName);
- }
- });
- }
- } else {
- resourceRule.resources.forEach(resource => resources.add(resource));
- }
-
- });
-
- return [...resources];
- } catch (error) {
- logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review: ${error}`, { namespace });
-
- return [];
- }
- };
- };
-};
-
-const authorizationNamespaceReviewInjectable = getInjectable({
- id: "authorization-namespace-review",
- instantiate: (di) => {
- const logger = di.inject(loggerInjectable);
-
- return authorizationNamespaceReview({ logger });
- },
-});
-
-export default authorizationNamespaceReviewInjectable;
diff --git a/src/common/cluster/cluster.ts b/src/common/cluster/cluster.ts
index fe66c9fe1b..303ee89361 100644
--- a/src/common/cluster/cluster.ts
+++ b/src/common/cluster/cluster.ts
@@ -9,8 +9,8 @@ import type { KubeConfig } from "@kubernetes/client-node";
import { HttpError } from "@kubernetes/client-node";
import type { Kubectl } from "../../main/kubectl/kubectl";
import type { KubeconfigManager } from "../../main/kubeconfig-manager/kubeconfig-manager";
-import type { KubeApiResource, KubeResource } from "../rbac";
-import { apiResourceRecord, apiResources } from "../rbac";
+import type { KubeApiResource, KubeApiResourceDescriptor } from "../rbac";
+import { formatKubeApiResource } from "../rbac";
import type { VersionDetector } from "../../main/cluster-detectors/version-detector";
import type { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
import plimit from "p-limit";
@@ -25,8 +25,8 @@ import assert from "assert";
import type { Logger } from "../logger";
import type { BroadcastMessage } from "../ipc/broadcast-message.injectable";
import type { LoadConfigfromFile } from "../kube-helpers/load-config-from-file.injectable";
-import type { RequestNamespaceResources } from "./authorization-namespace-review.injectable";
-import type { RequestListApiResources } from "./list-api-resources.injectable";
+import type { CanListResource, RequestNamespaceListPermissions, RequestNamespaceListPermissionsFor } from "./request-namespace-list-permissions.injectable";
+import type { RequestApiResources } from "./request-api-resources.injectable";
export interface ClusterDependencies {
readonly directoryForKubeConfigs: string;
@@ -36,8 +36,8 @@ export interface ClusterDependencies {
createContextHandler: (cluster: Cluster) => ClusterContextHandler;
createKubectl: (clusterVersion: string) => Kubectl;
createAuthorizationReview: (config: KubeConfig) => CanI;
- createAuthorizationNamespaceReview: (config: KubeConfig) => RequestNamespaceResources;
- createListApiResources: (cluster: Cluster) => RequestListApiResources;
+ requestApiResources: RequestApiResources;
+ requestNamespaceListPermissionsFor: RequestNamespaceListPermissionsFor;
createListNamespaces: (config: KubeConfig) => ListNamespaces;
createVersionDetector: (cluster: Cluster) => VersionDetector;
broadcastMessage: BroadcastMessage;
@@ -49,7 +49,7 @@ export interface ClusterDependencies {
*
* @beta
*/
-export class Cluster implements ClusterModel, ClusterState {
+export class Cluster implements ClusterModel {
/** Unique id for a cluster */
public readonly id: ClusterId;
private kubeCtl: Kubectl | undefined;
@@ -62,7 +62,6 @@ export class Cluster implements ClusterModel, ClusterState {
protected readonly _proxyKubeconfigManager: KubeconfigManager | undefined;
protected readonly eventsDisposer = disposer();
protected activated = false;
- private readonly resourceAccessStatuses = new Map();
public get contextHandler() {
// TODO: remove these once main/renderer are seperate classes
@@ -163,25 +162,21 @@ export class Cluster implements ClusterModel, ClusterState {
* @observable
*/
@observable metadata: ClusterMetadata = {};
+
/**
* List of allowed namespaces verified via K8S::SelfSubjectAccessReview api
- *
- * @observable
*/
- @observable allowedNamespaces: string[] = [];
- /**
- * List of allowed resources
- *
- * @observable
- * @internal
- */
- @observable allowedResources: string[] = [];
+ readonly allowedNamespaces = observable.array();
+
/**
* List of accessible namespaces provided by user in the Cluster Settings
- *
- * @observable
*/
- @observable accessibleNamespaces: string[] = [];
+ readonly accessibleNamespaces = observable.array();
+
+ private readonly knownResources = observable.array();
+
+ // The formatting of this is `group.name` or `name` (if in core)
+ private readonly allowedResources = observable.set();
/**
* Labels for the catalog entity
@@ -299,7 +294,7 @@ export class Cluster implements ClusterModel, ClusterState {
}
if (model.accessibleNamespaces) {
- this.accessibleNamespaces = model.accessibleNamespaces;
+ this.accessibleNamespaces.replace(model.accessibleNamespaces);
}
if (model.labels) {
@@ -433,8 +428,7 @@ export class Cluster implements ClusterModel, ClusterState {
this.accessible = false;
this.ready = false;
this.activated = false;
- this.allowedNamespaces = [];
- this.resourceAccessStatuses.clear();
+ this.allowedNamespaces.clear();
this.dependencies.logger.info(`[CLUSTER]: disconnected`, { id: this.id });
}
@@ -474,8 +468,7 @@ export class Cluster implements ClusterModel, ClusterState {
this.dependencies.logger.info(`[CLUSTER]: refreshAccessibility`, this.getMeta());
const proxyConfig = await this.getProxyKubeconfig();
const canI = this.dependencies.createAuthorizationReview(proxyConfig);
- const requestNamespaceResources = this.dependencies.createAuthorizationNamespaceReview(proxyConfig);
- const listApiResources = this.dependencies.createListApiResources(this);
+ const requestNamespaceListPermissions = this.dependencies.requestNamespaceListPermissionsFor(proxyConfig);
this.isAdmin = await canI({
namespace: "kube-system",
@@ -486,8 +479,9 @@ export class Cluster implements ClusterModel, ClusterState {
verb: "watch",
resource: "*",
});
- this.allowedNamespaces = await this.getAllowedNamespaces(proxyConfig);
- this.allowedResources = await this.getAllowedResources(listApiResources, requestNamespaceResources);
+ this.allowedNamespaces.replace(await this.requestAllowedNamespaces(proxyConfig));
+ this.knownResources.replace(await this.dependencies.requestApiResources(this));
+ this.allowedResources.replace(await this.getAllowedResources(requestNamespaceListPermissions));
this.ready = true;
}
@@ -600,7 +594,7 @@ export class Cluster implements ClusterModel, ClusterState {
accessible: this.accessible,
isAdmin: this.isAdmin,
allowedNamespaces: this.allowedNamespaces,
- allowedResources: this.allowedResources,
+ allowedResources: [...this.allowedResources],
isGlobalWatchEnabled: this.isGlobalWatchEnabled,
});
}
@@ -611,8 +605,8 @@ export class Cluster implements ClusterModel, ClusterState {
*/
@action setState(state: ClusterState) {
this.accessible = state.accessible;
- this.allowedNamespaces = state.allowedNamespaces;
- this.allowedResources = state.allowedResources;
+ this.allowedNamespaces.replace(state.allowedNamespaces);
+ this.allowedResources.replace(state.allowedResources);
this.apiUrl = state.apiUrl;
this.disconnected = state.disconnected;
this.isAdmin = state.isAdmin;
@@ -644,7 +638,7 @@ export class Cluster implements ClusterModel, ClusterState {
this.dependencies.broadcastMessage(`cluster:${this.id}:connection-update`, update);
}
- protected async getAllowedNamespaces(proxyConfig: KubeConfig) {
+ protected async requestAllowedNamespaces(proxyConfig: KubeConfig) {
if (this.accessibleNamespaces.length) {
return this.accessibleNamespaces;
}
@@ -668,69 +662,28 @@ export class Cluster implements ClusterModel, ClusterState {
}
}
- protected async getAllowedResources(listApiResources:RequestListApiResources, requestNamespaceResources: RequestNamespaceResources) {
+ protected async getAllowedResources(requestNamespaceListPermissions: RequestNamespaceListPermissions) {
+ if (!this.allowedNamespaces.length) {
+ return [];
+ }
+
try {
- if (!this.allowedNamespaces.length) {
- return [];
- }
+ const apiLimit = plimit(5); // 5 concurrent api requests
+ const canListResourceCheckers = await Promise.all((
+ this.allowedNamespaces.map(namespace => apiLimit(() => requestNamespaceListPermissions(namespace)))
+ ));
+ const canListNamespacedResource: CanListResource = (resource) => canListResourceCheckers.some(fn => fn(resource));
- const unknownResources = new Map(apiResources.map(resource => ([resource.apiName, resource])));
-
- const availableResources = await listApiResources();
- const availableResourcesNames = new Set(availableResources.map(apiResource => apiResource.apiName));
-
- [...unknownResources.values()].map(unknownResource => {
- if (!availableResourcesNames.has(unknownResource.apiName)) {
- this.resourceAccessStatuses.set(unknownResource, false);
- unknownResources.delete(unknownResource.apiName);
- }
- });
-
- if (unknownResources.size > 0) {
- const apiLimit = plimit(5); // 5 concurrent api requests
-
- await Promise.all(this.allowedNamespaces.map(namespace => apiLimit(async () => {
- if (unknownResources.size === 0) {
- return;
- }
-
- const namespaceResources = await requestNamespaceResources(namespace, availableResources);
-
- for (const resourceName of namespaceResources) {
- const unknownResource = unknownResources.get(resourceName);
-
- if (unknownResource) {
- this.resourceAccessStatuses.set(unknownResource, true);
- unknownResources.delete(resourceName);
- }
- }
- })));
-
- for (const forbiddenResource of unknownResources.values()) {
- this.resourceAccessStatuses.set(forbiddenResource, false);
- }
- }
-
- return apiResources
- .filter((resource) => this.resourceAccessStatuses.get(resource))
- .map(apiResource => apiResource.apiName);
+ return this.knownResources
+ .filter(canListNamespacedResource)
+ .map(formatKubeApiResource);
} catch (error) {
return [];
}
}
- isAllowedResource(kind: string): boolean {
- if ((kind as KubeResource) in apiResourceRecord) {
- return this.allowedResources.includes(kind);
- }
-
- const apiResource = apiResources.find(resource => resource.kind === kind);
-
- if (apiResource) {
- return this.allowedResources.includes(apiResource.apiName);
- }
-
- return true; // allowed by default for other resources
+ shouldShowResource(resource: KubeApiResourceDescriptor): boolean {
+ return this.allowedResources.has(formatKubeApiResource(resource));
}
isMetricHidden(resource: ClusterMetricsResourceType): boolean {
diff --git a/src/common/cluster/is-allowed-resource.ts b/src/common/cluster/is-allowed-resource.ts
deleted file mode 100644
index 7a6a392f78..0000000000
--- a/src/common/cluster/is-allowed-resource.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import type { KubeResource } from "../rbac";
-import { apiResourceRecord, apiResources } from "../rbac";
-
-export const isAllowedResource = (allowedResources: string[]) => (kind: string): boolean => {
- if ((kind as KubeResource) in apiResourceRecord) {
- return allowedResources.includes(kind);
- }
-
- const apiResource = apiResources.find(resource => resource.kind === kind);
-
- if (apiResource) {
- return allowedResources.includes(apiResource.apiName);
- }
-
- return true; // allowed by default for other resources
-};
diff --git a/src/common/cluster/list-api-resources.injectable.ts b/src/common/cluster/list-api-resources.injectable.ts
deleted file mode 100644
index ed9d5c9c39..0000000000
--- a/src/common/cluster/list-api-resources.injectable.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type {
- V1APIGroupList,
- V1APIResourceList,
- V1APIVersions,
-} from "@kubernetes/client-node";
-import { getInjectable } from "@ogre-tools/injectable";
-import type { K8sRequest } from "../../main/k8s-request.injectable";
-import k8SRequestInjectable from "../../main/k8s-request.injectable";
-import type { Logger } from "../logger";
-import loggerInjectable from "../logger.injectable";
-import type { KubeApiResource, KubeResource } from "../rbac";
-import type { Cluster } from "./cluster";
-import plimit from "p-limit";
-
-export type RequestListApiResources = () => Promise;
-
-/**
- * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
- */
-export type ListApiResources = (cluster: Cluster) => RequestListApiResources;
-
-interface Dependencies {
- logger: Logger;
- k8sRequest: K8sRequest;
-}
-
-const listApiResources = ({ k8sRequest, logger }: Dependencies): ListApiResources => {
- return (cluster) => {
- const clusterRequest = (path: string) => k8sRequest(cluster, path);
- const apiLimit = plimit(5);
-
- return async () => {
- const resources: KubeApiResource[] = [];
-
- try {
- const resourceListGroups:{ group:string;path:string }[] = [];
-
- await Promise.all(
- [
- clusterRequest("/api").then((response:V1APIVersions)=>response.versions.forEach(version => resourceListGroups.push({ group:version, path:`/api/${version}` }))),
- clusterRequest("/apis").then((response:V1APIGroupList) => response.groups.forEach(group => {
- const preferredVersion = group.preferredVersion?.groupVersion;
-
- if (preferredVersion) {
- resourceListGroups.push({ group:group.name, path:`/apis/${preferredVersion}` });
- }
- })),
- ],
- );
-
- await Promise.all(
- resourceListGroups.map(({ group, path }) => apiLimit(async () => {
- const apiResources:V1APIResourceList = await clusterRequest(path);
-
- if (apiResources.resources) {
- resources.push(
- ...apiResources.resources.filter(resource => resource.verbs.includes("list")).map((resource) => ({
- apiName: resource.name as KubeResource,
- kind: resource.kind,
- group,
- })),
- );
- }
- }),
- ),
- );
- } catch (error) {
- logger.error(`[LIST-API-RESOURCES]: failed to list api resources: ${error}`);
- }
-
- return resources;
- };
- };
-};
-
-const listApiResourcesInjectable = getInjectable({
- id: "list-api-resources",
- instantiate: (di) => {
- const k8sRequest = di.inject(k8SRequestInjectable);
- const logger = di.inject(loggerInjectable);
-
- return listApiResources({ k8sRequest, logger });
- },
-});
-
-export default listApiResourcesInjectable;
diff --git a/src/common/cluster/request-api-resources.injectable.ts b/src/common/cluster/request-api-resources.injectable.ts
new file mode 100644
index 0000000000..3e1a621f39
--- /dev/null
+++ b/src/common/cluster/request-api-resources.injectable.ts
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { V1APIGroupList, V1APIResourceList, V1APIVersions } from "@kubernetes/client-node";
+import { getInjectable } from "@ogre-tools/injectable";
+import k8SRequestInjectable from "../../main/k8s-request.injectable";
+import loggerInjectable from "../logger.injectable";
+import type { KubeApiResource } from "../rbac";
+import type { Cluster } from "./cluster";
+import plimit from "p-limit";
+
+export type RequestApiResources = (cluster: Cluster) => Promise;
+
+interface KubeResourceListGroup {
+ group: string;
+ path: string;
+}
+
+const requestApiResourcesInjectable = getInjectable({
+ id: "request-api-resources",
+ instantiate: (di): RequestApiResources => {
+ const k8sRequest = di.inject(k8SRequestInjectable);
+ const logger = di.inject(loggerInjectable);
+
+ return async (cluster) => {
+ const apiLimit = plimit(5);
+ const kubeApiResources: KubeApiResource[] = [];
+ const resourceListGroups: KubeResourceListGroup[] = [];
+
+ try {
+ await Promise.all([
+ (async () => {
+ const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
+
+ for (const version of versions) {
+ resourceListGroups.push({
+ group: version,
+ path: `/api/${version}`,
+ });
+ }
+ })(),
+ (async () => {
+ const { groups } = await k8sRequest(cluster, "/apis") as V1APIGroupList;
+
+ for (const { preferredVersion, name } of groups) {
+ const { groupVersion } = preferredVersion ?? {};
+
+ if (groupVersion) {
+ resourceListGroups.push({
+ group: name,
+ path: `/apis/${groupVersion}`,
+ });
+ }
+ }
+ })(),
+ ]);
+
+ await Promise.all(
+ resourceListGroups.map(({ group, path }) => apiLimit(async () => {
+ const { resources } = await k8sRequest(cluster, path) as V1APIResourceList;
+
+ for (const resource of resources) {
+ kubeApiResources.push({
+ apiName: resource.name,
+ kind: resource.kind,
+ group,
+ namespaced: resource.namespaced,
+ });
+ }
+ })),
+ );
+ } catch (error) {
+ logger.error(`[LIST-API-RESOURCES]: failed to list api resources: ${error}`);
+ }
+
+ return kubeApiResources;
+ };
+ },
+});
+
+export default requestApiResourcesInjectable;
diff --git a/src/common/cluster/request-namespace-list-permissions.injectable.ts b/src/common/cluster/request-namespace-list-permissions.injectable.ts
new file mode 100644
index 0000000000..62d2477e42
--- /dev/null
+++ b/src/common/cluster/request-namespace-list-permissions.injectable.ts
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { KubeConfig } from "@kubernetes/client-node";
+import { AuthorizationV1Api } from "@kubernetes/client-node";
+import { getInjectable } from "@ogre-tools/injectable";
+import loggerInjectable from "../logger.injectable";
+import type { KubeApiResource } from "../rbac";
+
+export type CanListResource = (resource: KubeApiResource) => boolean;
+
+/**
+ * Requests the permissions for actions on the kube cluster
+ * @param namespace The namespace of the resources
+ */
+export type RequestNamespaceListPermissions = (namespace: string) => Promise;
+
+/**
+ * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
+ */
+export type RequestNamespaceListPermissionsFor = (proxyConfig: KubeConfig) => RequestNamespaceListPermissions;
+
+const requestNamespaceListPermissionsForInjectable = getInjectable({
+ id: "request-namespace-list-permissions-for",
+ instantiate: (di): RequestNamespaceListPermissionsFor => {
+ const logger = di.inject(loggerInjectable);
+
+ return (proxyConfig) => {
+ const api = proxyConfig.makeApiClient(AuthorizationV1Api);
+
+ return async (namespace) => {
+ try {
+ const { body: { status }} = await api.createSelfSubjectRulesReview({
+ apiVersion: "authorization.k8s.io/v1",
+ kind: "SelfSubjectRulesReview",
+ spec: { namespace },
+ });
+
+ if (!status || status.incomplete) {
+ logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
+
+ return () => true;
+ }
+
+ const { resourceRules } = status;
+
+ return (resource) => {
+ const resourceRule = resourceRules.find(({
+ apiGroups = [],
+ resources = [],
+ }) => {
+ const isAboutRelevantApiGroup = apiGroups.includes("*") || apiGroups.includes(resource.group);
+ const isAboutResource = resources.includes("*") || resources.includes(resource.apiName);
+
+ return isAboutRelevantApiGroup && isAboutResource;
+ });
+
+ if (!resourceRule) {
+ return false;
+ }
+
+ const { verbs } = resourceRule;
+
+ return verbs.includes("*") || verbs.includes("list");
+ };
+ } catch (error) {
+ logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error });
+
+ return () => true;
+ }
+ };
+ };
+ },
+});
+
+export default requestNamespaceListPermissionsForInjectable;
diff --git a/src/common/configure-packages.ts b/src/common/configure-packages.ts
deleted file mode 100644
index ec48be44ce..0000000000
--- a/src/common/configure-packages.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import * as Mobx from "mobx";
-import * as Immer from "immer";
-
-/**
- * Setup default configuration for external npm-packages
- */
-export default function configurePackages() {
- // Docs: https://mobx.js.org/configuration.html
- Mobx.configure({
- enforceActions: "never",
-
- // TODO: enable later (read more: https://mobx.js.org/migrating-from-4-or-5.html)
- // computedRequiresReaction: true,
- // reactionRequiresObservable: true,
- // observableRequiresReaction: true,
- });
-
- // Docs: https://immerjs.github.io/immer/
- // Required in `utils/storage-helper.ts`
- Immer.setAutoFreeze(false); // allow to merge mobx observables
- Immer.enableMapSet(); // allow to merge maps and sets
-}
diff --git a/src/common/fetch/download-json.injectable.ts b/src/common/fetch/download-json.injectable.ts
deleted file mode 100644
index 78a7d030d7..0000000000
--- a/src/common/fetch/download-json.injectable.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * 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 type { RequestInit, Response } from "node-fetch";
-import type { AsyncResult } from "../utils/async-result";
-import fetchInjectable from "./fetch.injectable";
-
-export interface DownloadJsonOptions {
- signal?: AbortSignal | null | undefined;
-}
-
-export type DownloadJson = (url: string, opts?: DownloadJsonOptions) => Promise>;
-
-const downloadJsonInjectable = getInjectable({
- id: "download-json",
- instantiate: (di): DownloadJson => {
- const fetch = di.inject(fetchInjectable);
-
- return async (url, opts) => {
- let result: Response;
-
- try {
- result = await fetch(url, opts as RequestInit);
- } catch (error) {
- return {
- callWasSuccessful: false,
- error: String(error),
- };
- }
-
- if (result.status < 200 || 300 <= result.status) {
- return {
- callWasSuccessful: false,
- error: result.statusText,
- };
- }
-
- try {
- return {
- callWasSuccessful: true,
- response: await result.json(),
- };
- } catch (error) {
- return {
- callWasSuccessful: false,
- error: String(error),
- };
- }
- };
- },
-});
-
-export default downloadJsonInjectable;
diff --git a/src/common/fetch/download-json/impl.ts b/src/common/fetch/download-json/impl.ts
new file mode 100644
index 0000000000..9faf9af124
--- /dev/null
+++ b/src/common/fetch/download-json/impl.ts
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { AsyncResult } from "../../utils/async-result";
+import type { Fetch } from "../fetch.injectable";
+import type { RequestInit, Response } from "node-fetch";
+
+export interface DownloadJsonOptions {
+ signal?: AbortSignal | null | undefined;
+}
+
+export type DownloadJson = (url: string, opts?: DownloadJsonOptions) => Promise>;
+
+export const downloadJsonWith = (fetch: Fetch): DownloadJson => async (url, opts) => {
+ let result: Response;
+
+ try {
+ result = await fetch(url, opts as RequestInit);
+ } catch (error) {
+ return {
+ callWasSuccessful: false,
+ error: String(error),
+ };
+ }
+
+ if (result.status < 200 || 300 <= result.status) {
+ return {
+ callWasSuccessful: false,
+ error: result.statusText,
+ };
+ }
+
+ try {
+ return {
+ callWasSuccessful: true,
+ response: await result.json(),
+ };
+ } catch (error) {
+ return {
+ callWasSuccessful: false,
+ error: String(error),
+ };
+ }
+};
+
diff --git a/src/common/fetch/download-json/normal.injectable.ts b/src/common/fetch/download-json/normal.injectable.ts
new file mode 100644
index 0000000000..adb5e35d82
--- /dev/null
+++ b/src/common/fetch/download-json/normal.injectable.ts
@@ -0,0 +1,14 @@
+/**
+ * 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 fetchInjectable from "../fetch.injectable";
+import { downloadJsonWith } from "./impl";
+
+const downloadJsonInjectable = getInjectable({
+ id: "download-json",
+ instantiate: (di) => downloadJsonWith(di.inject(fetchInjectable)),
+});
+
+export default downloadJsonInjectable;
diff --git a/src/common/fetch/download-json/proxy.injectable.ts b/src/common/fetch/download-json/proxy.injectable.ts
new file mode 100644
index 0000000000..46268d4ddb
--- /dev/null
+++ b/src/common/fetch/download-json/proxy.injectable.ts
@@ -0,0 +1,14 @@
+/**
+ * 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 proxyFetchInjectable from "../proxy-fetch.injectable";
+import { downloadJsonWith } from "./impl";
+
+const proxyDownloadJsonInjectable = getInjectable({
+ id: "proxy-download-json",
+ instantiate: (di) => downloadJsonWith(di.inject(proxyFetchInjectable)),
+});
+
+export default proxyDownloadJsonInjectable;
diff --git a/src/common/fetch/fetch-module.injectable.ts b/src/common/fetch/fetch-module.injectable.ts
new file mode 100644
index 0000000000..444333f196
--- /dev/null
+++ b/src/common/fetch/fetch-module.injectable.ts
@@ -0,0 +1,19 @@
+/**
+ * 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 type * as FetchModule from "node-fetch";
+
+const { NodeFetch } = require("../../../build/webpack/node-fetch.bundle") as { NodeFetch: typeof FetchModule };
+
+/**
+ * NOTE: while using this module can cause side effects, this specific injectable is not marked as
+ * such since sometimes the request can be wholely within the perview of unit test
+ */
+const nodeFetchModuleInjectable = getInjectable({
+ id: "node-fetch-module",
+ instantiate: () => NodeFetch,
+});
+
+export default nodeFetchModuleInjectable;
diff --git a/src/common/fetch/fetch.injectable.ts b/src/common/fetch/fetch.injectable.ts
index bd1ba89db7..d4f51efe0d 100644
--- a/src/common/fetch/fetch.injectable.ts
+++ b/src/common/fetch/fetch.injectable.ts
@@ -3,32 +3,17 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { HttpsProxyAgent } from "hpagent";
-import type * as FetchModule from "node-fetch";
-import userStoreInjectable from "../user-store/user-store.injectable";
-
-const { NodeFetch: { default: fetch }} = require("../../../build/webpack/node-fetch.bundle") as { NodeFetch: typeof FetchModule };
-
-type Response = FetchModule.Response;
-type RequestInit = FetchModule.RequestInit;
+import type { RequestInit, Response } from "node-fetch";
+import nodeFetchModuleInjectable from "./fetch-module.injectable";
export type Fetch = (url: string, init?: RequestInit) => Promise;
const fetchInjectable = getInjectable({
id: "fetch",
instantiate: (di): Fetch => {
- const { httpsProxy, allowUntrustedCAs } = di.inject(userStoreInjectable);
- const agent = httpsProxy
- ? new HttpsProxyAgent({
- proxy: httpsProxy,
- rejectUnauthorized: !allowUntrustedCAs,
- })
- : undefined;
+ const { default: fetch } = di.inject(nodeFetchModuleInjectable);
- return (url, init = {}) => fetch(url, {
- agent,
- ...init,
- });
+ return (url, init) => fetch(url, init);
},
causesSideEffects: true,
});
diff --git a/src/common/fetch/proxy-fetch.injectable.ts b/src/common/fetch/proxy-fetch.injectable.ts
new file mode 100644
index 0000000000..f13842c410
--- /dev/null
+++ b/src/common/fetch/proxy-fetch.injectable.ts
@@ -0,0 +1,30 @@
+/**
+ * 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 { HttpsProxyAgent } from "hpagent";
+import userStoreInjectable from "../user-store/user-store.injectable";
+import type { Fetch } from "./fetch.injectable";
+import fetchInjectable from "./fetch.injectable";
+
+const proxyFetchInjectable = getInjectable({
+ id: "proxy-fetch",
+ instantiate: (di): Fetch => {
+ const fetch = di.inject(fetchInjectable);
+ const { httpsProxy, allowUntrustedCAs } = di.inject(userStoreInjectable);
+ const agent = httpsProxy
+ ? new HttpsProxyAgent({
+ proxy: httpsProxy,
+ rejectUnauthorized: !allowUntrustedCAs,
+ })
+ : undefined;
+
+ return (url, init = {}) => fetch(url, {
+ agent,
+ ...init,
+ });
+ },
+});
+
+export default proxyFetchInjectable;
diff --git a/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts
index 3ab2bc515c..6ea03fff08 100644
--- a/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts
@@ -3,22 +3,19 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const configMapsRouteInjectable = getInjectable({
id: "config-maps-route",
-
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "configmaps");
-
- return {
- path: "/configmaps",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
-
+ instantiate: (di) => ({
+ path: "/configmaps",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "configmaps",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts
index fe1814734f..00002620ee 100644
--- a/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const horizontalPodAutoscalersRouteInjectable = getInjectable({
id: "horizontal-pod-autoscalers-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "horizontalpodautoscalers");
-
- return {
- path: "/hpa",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/hpa",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "horizontalpodautoscalers",
+ group: "autoscaling",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts
index 65ee0e3ffa..ea4eb2ae59 100644
--- a/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const leasesRouteInjectable = getInjectable({
id: "leases",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "leases");
-
- return {
- path: "/leases",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/leases",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "leases",
+ group: "coordination.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts
index bcf740113b..8623f3520e 100644
--- a/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts
@@ -3,24 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const limitRangesRouteInjectable = getInjectable({
id: "limit-ranges-route",
- instantiate: (di) => {
- const limitRangesIsAllowed = di.inject(
- isAllowedResourceInjectable,
- "limitranges",
- );
-
- return {
- path: "/limitranges",
- clusterFrame: true,
- isEnabled: limitRangesIsAllowed,
- };
- },
+ instantiate: (di) => ({
+ path: "/limitranges",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "limitranges",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts
index df6d89fb06..12ce0a2138 100644
--- a/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const podDisruptionBudgetsRouteInjectable = getInjectable({
id: "pod-disruption-budgets-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "poddisruptionbudgets");
-
- return {
- path: "/poddisruptionbudgets",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/poddisruptionbudgets",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "poddisruptionbudgets",
+ group: "policy",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts
index 1c3632c261..75194b0541 100644
--- a/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const priorityClassesRouteInjectable = getInjectable({
id: "priority-classes-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "priorityclasses");
-
- return {
- path: "/priorityclasses",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/priorityclasses",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "priorityclasses",
+ group: "scheduling.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts
index 496a42ed0c..209f77e19a 100644
--- a/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const resourceQuotasRouteInjectable = getInjectable({
id: "resource-quotas-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "resourcequotas");
-
- return {
- path: "/resourcequotas",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/resourcequotas",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "resourcequotas",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts
index 72934f9158..beab83754f 100644
--- a/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const runtimeClassesRouteInjectable = getInjectable({
id: "runtime-classes-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "runtimeclasses");
-
- return {
- path: "/runtimeclasses",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/runtimeclasses",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "runtimeclasses",
+ group: "node.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts
index f451f51d0c..079ddcbf83 100644
--- a/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const secretsRouteInjectable = getInjectable({
id: "secrets-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "secrets");
-
- return {
- path: "/secrets",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/secrets",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "secrets",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts b/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts
index a53cb5d1f8..b3df358ad8 100644
--- a/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
const eventsRouteInjectable = getInjectable({
id: "events-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "events");
-
- return {
- path: "/events",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/events",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "events",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts b/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts
index 395c128682..2aa6c23efe 100644
--- a/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
const namespacesRouteInjectable = getInjectable({
id: "namespaces-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "namespaces");
-
- return {
- path: "/namespaces",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/namespaces",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "namespaces",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts
index e30df3123b..c88ec04714 100644
--- a/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const endpointsRouteInjectable = getInjectable({
id: "endpoints-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "endpoints");
-
- return {
- path: "/endpoints",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/endpoints",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "endpoints",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts
index 12565deeaf..8e01646b82 100644
--- a/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts
@@ -3,21 +3,27 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
+import { computedOr } from "../../../../../utils/computed-or";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const ingressesRouteInjectable = getInjectable({
id: "ingresses-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "ingresses");
-
- return {
- path: "/ingresses",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/ingresses",
+ clusterFrame: true,
+ isEnabled: computedOr(
+ di.inject(shouldShowResourceInjectionToken, {
+ apiName: "ingresses",
+ group: "networking.k8s.io",
+ }),
+ di.inject(shouldShowResourceInjectionToken, {
+ apiName: "ingresses",
+ group: "extensions",
+ }),
+ ),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts
index ead62ee435..38a1b8a7e2 100644
--- a/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const networkPoliciesRouteInjectable = getInjectable({
id: "network-policies-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "networkpolicies");
-
- return {
- path: "/network-policies",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/network-policies",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "networkpolicies",
+ group: "networking.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts
index 033c95673b..53300ee241 100644
--- a/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const servicesRouteInjectable = getInjectable({
id: "services-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "services");
-
- return {
- path: "/services",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/services",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "services",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts
index febd733343..81323843d5 100644
--- a/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
const nodesRouteInjectable = getInjectable({
id: "nodes-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "nodes");
-
- return {
- path: "/nodes",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/nodes",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "nodes",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts b/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts
index 209fc113d8..8315fd7773 100644
--- a/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
const clusterOverviewRouteInjectable = getInjectable({
id: "cluster-overview-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "nodes");
-
- return {
- path: "/overview",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/overview",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "nodes",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts
index 8879541355..1b96933136 100644
--- a/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const persistentVolumeClaimsRouteInjectable = getInjectable({
id: "persistent-volume-claims-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "persistentvolumeclaims");
-
- return {
- path: "/persistent-volume-claims",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/persistent-volume-claims",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "persistentvolumeclaims",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts
index e6549ea45b..52f95b32c6 100644
--- a/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const persistentVolumesRouteInjectable = getInjectable({
id: "persistent-volumes-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "persistentvolumes");
-
- return {
- path: "/persistent-volumes",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/persistent-volumes",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "persistentvolumes",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts
index 69f2b5d4ee..8702ab1602 100644
--- a/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const storageClassesRouteInjectable = getInjectable({
id: "storage-classes-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "storageclasses");
-
- return {
- path: "/storage-classes",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/storage-classes",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "storageclasses",
+ group: "storage.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts
index f19491ee72..0903d5fced 100644
--- a/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const clusterRoleBindingsRouteInjectable = getInjectable({
id: "cluster-role-bindings-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "clusterrolebindings");
-
- return {
- path: "/cluster-role-bindings",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/cluster-role-bindings",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "clusterrolebindings",
+ group: "rbac.authorization.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts
index d21c2c33a4..9fce206667 100644
--- a/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const clusterRolesRouteInjectable = getInjectable({
id: "cluster-roles-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "clusterroles");
-
- return {
- path: "/cluster-roles",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/cluster-roles",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "clusterroles",
+ group: "rbac.authorization.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts
index 14cfcbedc5..2f35986916 100644
--- a/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts
@@ -3,19 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const podSecurityPoliciesRouteInjectable = getInjectable({
id: "pod-security-policies-route",
instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "podsecuritypolicies");
-
return {
path: "/pod-security-policies",
clusterFrame: true,
- isEnabled: isAllowedResource,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "podsecuritypolicies",
+ group: "policy",
+ }),
};
},
diff --git a/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts
index 0f908e5876..759c1b8eda 100644
--- a/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts
@@ -3,19 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const roleBindingsRouteInjectable = getInjectable({
id: "role-bindings-route",
instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "rolebindings");
-
return {
path: "/role-bindings",
clusterFrame: true,
- isEnabled: isAllowedResource,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "rolebindings",
+ group: "rbac.authorization.k8s.io",
+ }),
};
},
diff --git a/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts
index 94db156fa4..efe4cad810 100644
--- a/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const rolesRouteInjectable = getInjectable({
id: "roles-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "roles");
-
- return {
- path: "/roles",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/roles",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "roles",
+ group: "rbac.authorization.k8s.io",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts b/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts
index 4d79258c54..3bf6c1ec00 100644
--- a/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const serviceAccountsRouteInjectable = getInjectable({
id: "service-accounts-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "serviceaccounts");
-
- return {
- path: "/service-accounts",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/service-accounts",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "serviceaccounts",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts
index 735ea94642..33453a2247 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const cronJobsRouteInjectable = getInjectable({
id: "cron-jobs-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "cronjobs");
-
- return {
- path: "/cronjobs",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/cronjobs",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "cronjobs",
+ group: "batch",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts
index 55729813e8..f1ec2008fa 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const daemonsetsRouteInjectable = getInjectable({
id: "daemonsets-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "daemonsets");
-
- return {
- path: "/daemonsets",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/daemonsets",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "daemonsets",
+ group: "apps",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts
index b9ff072e66..84c059780f 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const deploymentsRouteInjectable = getInjectable({
id: "deployments-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "deployments");
-
- return {
- path: "/deployments",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/deployments",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "deployments",
+ group: "apps",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts
index d9190a7ea8..39cc89e88f 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const jobsRouteInjectable = getInjectable({
id: "jobs-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "jobs");
-
- return {
- path: "/jobs",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/jobs",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "jobs",
+ group: "batch",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts
index e9fb2a2b16..577f1c1a91 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const podsRouteInjectable = getInjectable({
id: "pods-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "pods");
-
- return {
- path: "/pods",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/pods",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "pods",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts
index 0319d27550..b790ce13ec 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const replicasetsRouteInjectable = getInjectable({
id: "replicasets-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "replicasets");
-
- return {
- path: "/replicasets",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/replicasets",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "replicasets",
+ group: "apps",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts b/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts
index a3089fa62f..72c81b3bee 100644
--- a/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts
+++ b/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts
@@ -3,21 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
+import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const statefulsetsRouteInjectable = getInjectable({
id: "statefulsets-route",
- instantiate: (di) => {
- const isAllowedResource = di.inject(isAllowedResourceInjectable, "statefulsets");
-
- return {
- path: "/statefulsets",
- clusterFrame: true,
- isEnabled: isAllowedResource,
- };
- },
+ instantiate: (di) => ({
+ path: "/statefulsets",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "statefulsets",
+ group: "apps",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/common/k8s-api/__tests__/api-manager.test.ts b/src/common/k8s-api/__tests__/api-manager.test.ts
index 3e411b6584..e99ac62018 100644
--- a/src/common/k8s-api/__tests__/api-manager.test.ts
+++ b/src/common/k8s-api/__tests__/api-manager.test.ts
@@ -3,7 +3,14 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
+import type { DiContainer } from "@ogre-tools/injectable";
+import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
+import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
+import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
+import directoryForKubeConfigsInjectable from "../../app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
+import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import type { ApiManager } from "../api-manager";
import apiManagerInjectable from "../api-manager/manager.injectable";
import { KubeApi } from "../kube-api";
@@ -22,9 +29,24 @@ class TestStore extends KubeObjectStore {
describe("ApiManager", () => {
let apiManager: ApiManager;
+ let di: DiContainer;
beforeEach(() => {
- const di = getDiForUnitTesting({ doGeneralOverrides: true });
+ 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",
+ }));
apiManager = di.inject(apiManagerInjectable);
});
@@ -40,7 +62,9 @@ describe("ApiManager", () => {
fallbackApiBases: [fallbackApiBase],
checkPreferredVersion: true,
});
- const kubeStore = new TestStore(kubeApi);
+ const kubeStore = new TestStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, kubeApi);
apiManager.registerApi(apiBase, kubeApi);
diff --git a/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts b/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts
index eb13464716..e2caef39fa 100644
--- a/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts
+++ b/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts
@@ -3,43 +3,23 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { KubeJsonApi } from "../kube-json-api";
-import { PassThrough } from "stream";
import type { ApiManager } from "../api-manager";
import { Ingress, IngressApi } from "../endpoints";
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
import apiManagerInjectable from "../api-manager/manager.injectable";
-import autoRegistrationInjectable from "../api-manager/auto-registration.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 type { Response, Headers as NodeFetchHeaders } from "node-fetch";
-
-const createMockResponseFromString = (url: string, data: string, statusCode = 200) => {
- const res: jest.Mocked = {
- buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
- clone: jest.fn(() => res),
- arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
- blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
- body: new PassThrough(),
- bodyUsed: false,
- headers: new Headers() as NodeFetchHeaders,
- json: jest.fn(async () => JSON.parse(await res.text())),
- ok: 200 <= statusCode && statusCode < 300,
- redirected: 300 <= statusCode && statusCode < 400,
- size: data.length,
- status: statusCode,
- statusText: "some-text",
- text: jest.fn(async () => data),
- type: "basic",
- url,
- formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
- };
-
- return res;
-};
+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";
+import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
+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";
describe("KubeApi", () => {
let request: KubeJsonApi;
@@ -52,6 +32,20 @@ describe("KubeApi", () => {
fetchMock = asyncFn();
di.override(fetchInjectable, () => fetchMock);
+ 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",
+ }));
+
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
request = createKubeJsonApi({
@@ -60,7 +54,9 @@ describe("KubeApi", () => {
});
registerApiSpy = jest.spyOn(di.inject(apiManagerInjectable), "registerApi");
- di.inject(autoRegistrationInjectable);
+ const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
+
+ setupAutoRegistration.run();
});
describe("on first call to IngressApi.get()", () => {
diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts
index 4f67ed1401..a4f9fd5b21 100644
--- a/src/common/k8s-api/__tests__/kube-api.test.ts
+++ b/src/common/k8s-api/__tests__/kube-api.test.ts
@@ -8,7 +8,6 @@ import type { KubeJsonApi, KubeJsonApiData } from "../kube-json-api";
import { PassThrough } from "stream";
import { Deployment, DeploymentApi, NamespaceApi, Pod, PodApi } from "../endpoints";
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
-import autoRegistrationInjectable from "../api-manager/auto-registration.injectable";
import type { Fetch } from "../../fetch/fetch.injectable";
import fetchInjectable from "../../fetch/fetch.injectable";
import type { CreateKubeApiForRemoteCluster } from "../create-kube-api-for-remote-cluster.injectable";
@@ -19,64 +18,14 @@ import { flushPromises } from "../../test-utils/flush-promises";
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
import type { IKubeWatchEvent } from "../kube-watch-event";
import type { KubeJsonApiDataFor } from "../kube-object";
-import type { Response, Headers as NodeFetchHeaders } from "node-fetch";
import AbortController from "abort-controller";
-
-const createMockResponseFromString = (url: string, data: string, statusCode = 200) => {
- const res: jest.Mocked = {
- buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
- clone: jest.fn(() => res),
- arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
- blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
- body: new PassThrough(),
- bodyUsed: false,
- headers: new Headers() as NodeFetchHeaders,
- json: jest.fn(async () => JSON.parse(await res.text())),
- ok: 200 <= statusCode && statusCode < 300,
- redirected: 300 <= statusCode && statusCode < 400,
- size: data.length,
- status: statusCode,
- statusText: "some-text",
- text: jest.fn(async () => data),
- type: "basic",
- url,
- formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
- };
-
- return res;
-};
-
-const createMockResponseFromStream = (url: string, stream: NodeJS.ReadableStream, statusCode = 200) => {
- const res: jest.Mocked = {
- buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }),
- clone: jest.fn(() => res),
- arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }),
- blob: jest.fn(async () => { throw new Error("blob() is not supported"); }),
- body: stream,
- bodyUsed: false,
- headers: new Headers() as NodeFetchHeaders,
- json: jest.fn(async () => JSON.parse(await res.text())),
- ok: 200 <= statusCode && statusCode < 300,
- redirected: 300 <= statusCode && statusCode < 400,
- size: 10,
- status: statusCode,
- statusText: "some-text",
- text: jest.fn(() => {
- const chunks: Buffer[] = [];
-
- return new Promise((resolve, reject) => {
- stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
- stream.on("error", (err) => reject(err));
- stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
- });
- }),
- type: "basic",
- url,
- formData: jest.fn(async () => { throw new Error("formData() is not supported"); }),
- };
-
- return res;
-};
+import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable";
+import { createMockResponseFromStream, createMockResponseFromString } from "../../../test-utils/mock-responses";
+import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
+import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
+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";
describe("createKubeApiForRemoteCluster", () => {
let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster;
@@ -85,6 +34,20 @@ describe("createKubeApiForRemoteCluster", () => {
beforeEach(async () => {
const 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",
+ }));
+
fetchMock = asyncFn();
di.override(fetchInjectable, () => fetchMock);
@@ -174,6 +137,20 @@ describe("KubeApi", () => {
beforeEach(async () => {
const 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",
+ }));
+
fetchMock = asyncFn();
di.override(fetchInjectable, () => fetchMock);
@@ -184,7 +161,9 @@ describe("KubeApi", () => {
apiBase: "/api-kube",
});
- di.inject(autoRegistrationInjectable);
+ const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
+
+ setupAutoRegistration.run();
});
describe("patching deployments", () => {
diff --git a/src/common/k8s-api/__tests__/kube-object.store.test.ts b/src/common/k8s-api/__tests__/kube-object.store.test.ts
index 91ed80fbde..afe755a6ba 100644
--- a/src/common/k8s-api/__tests__/kube-object.store.test.ts
+++ b/src/common/k8s-api/__tests__/kube-object.store.test.ts
@@ -3,27 +3,22 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { Cluster } from "../../cluster/cluster";
-import type { ClusterContext } from "../cluster-context";
import type { KubeApi } from "../kube-api";
import { KubeObject } from "../kube-object";
import type { KubeObjectStoreLoadingParams } from "../kube-object.store";
import { KubeObjectStore } from "../kube-object.store";
class FakeKubeObjectStore extends KubeObjectStore {
- _context = {
- allNamespaces: [],
- contextNamespaces: [],
- hasSelectedAll: false,
- cluster: {} as Cluster,
- } as ClusterContext;
-
- get context() {
- return this._context;
- }
-
constructor(private readonly _loadItems: (params: KubeObjectStoreLoadingParams) => KubeObject[], api: Partial>) {
- super(api as KubeApi);
+ super({
+ context: {
+ allNamespaces: [],
+ contextNamespaces: [],
+ hasSelectedAll: false,
+ isGlobalWatchEnabled: () => true,
+ isLoadingAll: () => true,
+ },
+ }, api as KubeApi);
}
async loadItems(params: KubeObjectStoreLoadingParams) {
diff --git a/src/common/k8s-api/api-base-configs.ts b/src/common/k8s-api/api-base-configs.ts
new file mode 100644
index 0000000000..5ac67229ec
--- /dev/null
+++ b/src/common/k8s-api/api-base-configs.ts
@@ -0,0 +1,14 @@
+/**
+ * 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";
+
+export const apiBaseServerAddressInjectionToken = getInjectionToken({
+ id: "api-base-config-server-address-token",
+});
+
+export const apiBaseHostHeaderInjectionToken = getInjectionToken({
+ id: "api-base-host-header-token",
+});
diff --git a/src/common/k8s-api/api-base.injectable.ts b/src/common/k8s-api/api-base.injectable.ts
new file mode 100644
index 0000000000..b340882672
--- /dev/null
+++ b/src/common/k8s-api/api-base.injectable.ts
@@ -0,0 +1,33 @@
+/**
+ * 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 { apiPrefix } from "../vars";
+import isDebuggingInjectable from "../vars/is-debugging.injectable";
+import isDevelopmentInjectable from "../vars/is-development.injectable";
+import { apiBaseHostHeaderInjectionToken, apiBaseServerAddressInjectionToken } from "./api-base-configs";
+import createJsonApiInjectable from "./create-json-api.injectable";
+
+const apiBaseInjectable = getInjectable({
+ id: "api-base",
+ instantiate: (di) => {
+ const createJsonApi = di.inject(createJsonApiInjectable);
+ const isDebugging = di.inject(isDebuggingInjectable);
+ const isDevelopment = di.inject(isDevelopmentInjectable);
+ const serverAddress = di.inject(apiBaseServerAddressInjectionToken);
+ const hostHeaderValue = di.inject(apiBaseHostHeaderInjectionToken);
+
+ return createJsonApi({
+ serverAddress,
+ apiBase: apiPrefix,
+ debug: isDevelopment || isDebugging,
+ }, {
+ headers: {
+ "Host": hostHeaderValue,
+ },
+ });
+ },
+});
+
+export default apiBaseInjectable;
diff --git a/src/common/k8s-api/api-base.ts b/src/common/k8s-api/api-base.ts
deleted file mode 100644
index 9544e4f60c..0000000000
--- a/src/common/k8s-api/api-base.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { JsonApi } from "./json-api";
-import { getInjectionToken } from "@ogre-tools/injectable";
-
-export const apiBaseInjectionToken = getInjectionToken({
- id: "api-base-token",
-});
diff --git a/src/common/k8s-api/api-manager/auto-registration.injectable.ts b/src/common/k8s-api/api-manager/auto-registration.injectable.ts
deleted file mode 100644
index 0cf1a3055d..0000000000
--- a/src/common/k8s-api/api-manager/auto-registration.injectable.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * 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 type { CustomResourceDefinition } from "../endpoints";
-import { KubeApi } from "../kube-api";
-import { KubeObject } from "../kube-object";
-import autoRegistrationEmitterInjectable from "./auto-registration-emitter.injectable";
-import apiManagerInjectable from "./manager.injectable";
-import { CustomResourceStore } from "./resource.store";
-
-const autoRegistrationInjectable = getInjectable({
- id: "api-manager-auto-registration",
- instantiate: (di) => {
- const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
- const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
- const beforeApiManagerInitializationApis: KubeApi[] = [];
- let initialized = false;
-
- const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
- const objectConstructor = class extends KubeObject {
- static readonly kind = crd.getResourceKind();
- static readonly namespaced = crd.isNamespaced();
- static readonly apiBase = crd.getResourceApiBase();
- };
-
- const api = (() => {
- const rawApi = apiManager.getApi(objectConstructor.apiBase);
-
- if (rawApi) {
- return rawApi;
- }
-
- const api = new KubeApi({ objectConstructor });
-
- apiManager.registerApi(api);
-
- return api;
- })();
-
- if (!apiManager.getStore(api)) {
- apiManager.registerStore(new CustomResourceStore(api));
- }
- };
- const autoInitKubeApi = (api: KubeApi) => {
- apiManager.registerApi(api);
- };
-
- autoRegistrationEmitter
- .on("customResourceDefinition", (crd) => {
- if (initialized) {
- autoInitCustomResourceStore(crd);
- } else {
- beforeApiManagerInitializationCrds.push(crd);
- }
- })
- .on("kubeApi", (api) => {
- if (initialized) {
- autoInitKubeApi(api);
- } else {
- beforeApiManagerInitializationApis.push(api);
- }
- });
-
- const apiManager = di.inject(apiManagerInjectable);
-
- beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
- beforeApiManagerInitializationApis.forEach(autoInitKubeApi);
- initialized = true;
- },
-});
-
-export default autoRegistrationInjectable;
diff --git a/src/common/k8s-api/api-manager/resource.store.ts b/src/common/k8s-api/api-manager/resource.store.ts
index 63ccdcf93d..c81ce7daec 100644
--- a/src/common/k8s-api/api-manager/resource.store.ts
+++ b/src/common/k8s-api/api-manager/resource.store.ts
@@ -4,11 +4,12 @@
*/
import type { KubeApi } from "../kube-api";
+import type { KubeObjectStoreDependencies } from "../kube-object.store";
import { KubeObjectStore } from "../kube-object.store";
import type { KubeObject } from "../kube-object";
export class CustomResourceStore extends KubeObjectStore> {
- constructor(api: KubeApi) {
- super(api);
+ constructor(deps: KubeObjectStoreDependencies, api: KubeApi) {
+ super(deps, api);
}
}
diff --git a/src/common/k8s-api/cluster-context.ts b/src/common/k8s-api/cluster-context.ts
deleted file mode 100644
index 098d92642d..0000000000
--- a/src/common/k8s-api/cluster-context.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { Cluster } from "../cluster/cluster";
-
-export interface ClusterContext {
- cluster: Cluster;
- allNamespaces: string[]; // available / allowed namespaces from cluster.ts
- contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
- hasSelectedAll: boolean;
-}
diff --git a/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts b/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts
index e7509dcbc7..eec3752e3a 100644
--- a/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts
+++ b/src/common/k8s-api/create-kube-api-for-cluster.injectable.ts
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import { apiKubePrefix } from "../vars";
import isDevelopmentInjectable from "../vars/is-development.injectable";
-import { apiBaseInjectionToken } from "./api-base";
+import apiBaseInjectable from "./api-base.injectable";
import createKubeJsonApiInjectable from "./create-kube-json-api.injectable";
import type { KubeApiOptions } from "./kube-api";
import { KubeApi } from "./kube-api";
@@ -33,7 +33,7 @@ export interface CreateKubeApiForCluster {
const createKubeApiForClusterInjectable = getInjectable({
id: "create-kube-api-for-cluster",
instantiate: (di): CreateKubeApiForCluster => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
const isDevelopment = di.inject(isDevelopmentInjectable);
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
diff --git a/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts b/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts
index 9901731c94..799b0bf963 100644
--- a/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts
+++ b/src/common/k8s-api/create-kube-json-api-for-cluster.injectable.ts
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import { apiKubePrefix } from "../vars";
import isDebuggingInjectable from "../vars/is-debugging.injectable";
-import { apiBaseInjectionToken } from "./api-base";
+import { apiBaseHostHeaderInjectionToken, apiBaseServerAddressInjectionToken } from "./api-base-configs";
import createKubeJsonApiInjectable from "./create-kube-json-api.injectable";
import type { KubeJsonApi } from "./kube-json-api";
@@ -14,19 +14,18 @@ export type CreateKubeJsonApiForCluster = (clusterId: string) => KubeJsonApi;
const createKubeJsonApiForClusterInjectable = getInjectable({
id: "create-kube-json-api-for-cluster",
instantiate: (di): CreateKubeJsonApiForCluster => {
- const apiBase = di.inject(apiBaseInjectionToken);
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
const isDebugging = di.inject(isDebuggingInjectable);
return (clusterId) => createKubeJsonApi(
{
- serverAddress: apiBase.config.serverAddress,
+ serverAddress: di.inject(apiBaseServerAddressInjectionToken),
apiBase: apiKubePrefix,
debug: isDebugging,
},
{
headers: {
- "Host": `${clusterId}.localhost:${new URL(apiBase.config.serverAddress).port}`,
+ "Host": `${clusterId}.${di.inject(apiBaseHostHeaderInjectionToken)}`,
},
},
);
diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts
index 89de5f9d17..4d9bfc55b1 100644
--- a/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-charts.api/request-charts.injectable.ts
@@ -3,10 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { apiBaseInjectionToken } from "../../api-base";
import type { RawHelmChart } from "../helm-charts.api";
import { HelmChart } from "../helm-charts.api";
import { isDefined } from "../../../utils";
+import apiBaseInjectable from "../../api-base.injectable";
export type RequestHelmCharts = () => Promise;
export type RepoHelmChartList = Record;
@@ -17,7 +17,7 @@ export type RepoHelmChartList = Record;
const requestHelmChartsInjectable = getInjectable({
id: "request-helm-charts",
instantiate: (di) => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return async () => {
const data = await apiBase.get>("/v2/charts");
diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts
index c6815c4b93..fb8eaafa10 100644
--- a/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-charts.api/request-readme.injectable.ts
@@ -3,17 +3,18 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
+import type { AsyncResult } from "../../../utils/async-result";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
const requestReadmeEndpoint = urlBuilderFor("/v2/charts/:repo/:name/readme");
-export type RequestHelmChartReadme = (repo: string, name: string, version?: string) => Promise;
+export type RequestHelmChartReadme = (repo: string, name: string, version?: string) => Promise>;
const requestHelmChartReadmeInjectable = getInjectable({
id: "request-helm-chart-readme",
instantiate: (di): RequestHelmChartReadme => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (repo, name, version) => (
apiBase.get(requestReadmeEndpoint.compile({ name, repo }, { version }))
diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts
index ec927fc37a..71105c9ff9 100644
--- a/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-charts.api/request-values.injectable.ts
@@ -3,17 +3,18 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
+import type { AsyncResult } from "../../../utils/async-result";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
const requestValuesEndpoint = urlBuilderFor("/v2/charts/:repo/:name/values");
-export type RequestHelmChartValues = (repo: string, name: string, version: string) => Promise;
+export type RequestHelmChartValues = (repo: string, name: string, version: string) => Promise>;
const requestHelmChartValuesInjectable = getInjectable({
id: "request-helm-chart-values",
instantiate: (di): RequestHelmChartValues => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (repo, name, version) => (
apiBase.get(requestValuesEndpoint.compile({ repo, name }, { version }))
diff --git a/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts b/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts
index 410d8ea596..ab85594ec6 100644
--- a/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-charts.api/request-versions.injectable.ts
@@ -4,10 +4,10 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
import { HelmChart } from "../helm-charts.api";
import type { RawHelmChart } from "../helm-charts.api";
import { isDefined } from "../../../utils";
+import apiBaseInjectable from "../../api-base.injectable";
const requestVersionsEndpoint = urlBuilderFor("/v2/charts/:repo/:name/versions");
@@ -16,7 +16,7 @@ export type RequestHelmChartVersions = (repo: string, chartName: string) => Prom
const requestHelmChartVersionsInjectable = getInjectable({
id: "request-helm-chart-versions",
instantiate: (di): RequestHelmChartVersions => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return async (repo, name) => {
const rawVersions = await apiBase.get(requestVersionsEndpoint.compile({ name, repo })) as RawHelmChart[];
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts
index 1bfe168b11..e1581c5d76 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
export type RequestHelmReleaseConfiguration = (
name: string,
@@ -18,7 +18,7 @@ const requestHelmReleaseConfigurationInjectable = getInjectable({
id: "request-helm-release-configuration",
instantiate: (di): RequestHelmReleaseConfiguration => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (name, namespace, all: boolean) => (
apiBase.get(requestConfigurationEnpoint.compile({ name, namespace }, { all }))
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts
index bad802d3cc..c1cd09d40f 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-create.injectable.ts
@@ -5,8 +5,8 @@
import yaml from "js-yaml";
import { getInjectable } from "@ogre-tools/injectable";
import type { HelmReleaseUpdateDetails } from "../helm-releases.api";
-import { apiBaseInjectionToken } from "../../api-base";
import { urlBuilderFor } from "../../../utils/buildUrl";
+import apiBaseInjectable from "../../api-base.injectable";
interface HelmReleaseCreatePayload {
name?: string;
@@ -25,7 +25,7 @@ const requestCreateHelmReleaseInjectable = getInjectable({
id: "request-create-helm-release",
instantiate: (di): RequestCreateHelmRelease => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return ({ repo, chart, values, ...data }) => {
return apiBase.post(requestCreateEndpoint.compile({}), {
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts
index 66b2013770..44af4311a9 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-delete.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
export type RequestDeleteHelmRelease = (name: string, namespace: string) => Promise;
@@ -13,7 +13,7 @@ const requestDeleteEndpoint = urlBuilderFor("/v2/releases/:namespace/:name");
const requestDeleteHelmReleaseInjectable = getInjectable({
id: "request-delete-helm-release",
instantiate: (di): RequestDeleteHelmRelease => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (name, namespace) => apiBase.del(requestDeleteEndpoint.compile({ name, namespace }));
},
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts
index 0b712cd78e..37f2287377 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-details.injectable.ts
@@ -4,8 +4,8 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { KubeJsonApiData } from "../../kube-json-api";
-import { apiBaseInjectionToken } from "../../api-base";
import { urlBuilderFor } from "../../../utils/buildUrl";
+import apiBaseInjectable from "../../api-base.injectable";
export interface HelmReleaseDetails {
resources: KubeJsonApiData[];
@@ -32,7 +32,7 @@ const requestHelmReleaseDetailsInjectable = getInjectable({
id: "call-for-helm-release-details",
instantiate: (di): CallForHelmReleaseDetails => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (name, namespace) => apiBase.get(requestDetailsEnpoint.compile({ name, namespace }));
},
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts
index b6e9794fe7..58b6a37dbb 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-history.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
export interface HelmReleaseRevision {
revision: number;
@@ -22,7 +22,7 @@ const requestHistoryEnpoint = urlBuilderFor("/v2/releases/:namespace/:name/histo
const requestHelmReleaseHistoryInjectable = getInjectable({
id: "request-helm-release-history",
instantiate: (di): RequestHelmReleaseHistory => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (name, namespace) => apiBase.get(requestHistoryEnpoint.compile({ name, namespace }));
},
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts
index 2619e289c6..ee6503ca99 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-releases.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
import type { HelmReleaseDto } from "../helm-releases.api";
export type RequestHelmReleases = (namespace?: string) => Promise;
@@ -15,7 +15,7 @@ const requestHelmReleasesInjectable = getInjectable({
id: "request-helm-releases",
instantiate: (di): RequestHelmReleases => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (namespace) => apiBase.get(requestHelmReleasesEndpoint.compile({ namespace }));
},
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts
index 1a9229b73a..036b399ef2 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
export type RequestHelmReleaseRollback = (name: string, namespace: string, revision: number) => Promise;
@@ -13,7 +13,7 @@ const requestRollbackEndpoint = urlBuilderFor("/v2/releases/:namespace/:name");
const requestHelmReleaseRollbackInjectable = getInjectable({
id: "request-helm-release-rollback",
instantiate: (di): RequestHelmReleaseRollback => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return async (name, namespace, revision) => {
await apiBase.put(
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts
index 8f46a3a210..715a21cea9 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-update.injectable.ts
@@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { apiBaseInjectionToken } from "../../api-base";
import { urlBuilderFor } from "../../../utils/buildUrl";
import type { AsyncResult } from "../../../utils/async-result";
+import apiBaseInjectable from "../../api-base.injectable";
interface HelmReleaseUpdatePayload {
repo: string;
@@ -26,7 +26,7 @@ const requestHelmReleaseUpdateInjectable = getInjectable({
id: "request-helm-release-update",
instantiate: (di): RequestHelmReleaseUpdate => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return async (name, namespace, { repo, chart, values, version }) => {
try {
diff --git a/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts b/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts
index d30f9973c4..99f1cc17d0 100644
--- a/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts
+++ b/src/common/k8s-api/endpoints/helm-releases.api/request-values.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { urlBuilderFor } from "../../../utils/buildUrl";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
export type RequestHelmReleaseValues = (name: string, namespace: string, all?: boolean) => Promise;
@@ -13,7 +13,7 @@ const requestValuesEndpoint = urlBuilderFor("/v2/release/:namespace/:name/values
const requestHelmReleaseValuesInjectable = getInjectable({
id: "request-helm-release-values",
instantiate: (di): RequestHelmReleaseValues => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (name, namespace, all) => apiBase.get(requestValuesEndpoint.compile({ name, namespace }, { all }));
},
diff --git a/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts
index 0da0bc95ec..e83c52b9aa 100644
--- a/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts
+++ b/src/common/k8s-api/endpoints/metrics.api/request-metrics.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { getSecondsFromUnixEpoch } from "../../../utils/date/get-current-date-time";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
import type { MetricData } from "../metrics.api";
@@ -46,7 +46,7 @@ export interface RequestMetrics {
const requestMetricsInjectable = getInjectable({
id: "request-metrics",
instantiate: (di) => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return (async (query: object, params: RequestMetricsParams = {}) => {
const { range = 3600, step = 60, namespace } = params;
diff --git a/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts b/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts
index 4711333ca6..0c74c4d58d 100644
--- a/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts
+++ b/src/common/k8s-api/endpoints/metrics.api/request-providers.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
export interface MetricProviderInfo {
name: string;
@@ -17,7 +17,7 @@ export type RequestMetricsProviders = () => Promise;
const requestMetricsProvidersInjectable = getInjectable({
id: "request-metrics-providers",
instantiate: (di): RequestMetricsProviders => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
return () => apiBase.get("/metrics/providers");
},
diff --git a/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts b/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts
index c8ad3435fd..49271fb6d2 100644
--- a/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts
+++ b/src/common/k8s-api/endpoints/resource-applier.api/request-patch.injectable.ts
@@ -4,26 +4,45 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { Patch } from "rfc6902";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
+import type { AsyncResult } from "../../../utils/async-result";
import type { KubeJsonApiData } from "../../kube-json-api";
-export type RequestKubeObjectPatch = (name: string, kind: string, ns: string | undefined, patch: Patch) => Promise;
+export type RequestKubeObjectPatch = (name: string, kind: string, ns: string | undefined, patch: Patch) => Promise>;
const requestKubeObjectPatchInjectable = getInjectable({
id: "request-kube-object-patch",
instantiate: (di): RequestKubeObjectPatch => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
- return (name, kind, ns, patch) => (
- apiBase.patch("/stack", {
+ return async (name, kind, ns, patch) => {
+ const result = await apiBase.patch("/stack", {
data: {
name,
kind,
ns,
patch,
},
- })
- );
+ }) as AsyncResult;
+
+ if (!result.callWasSuccessful) {
+ return result;
+ }
+
+ try {
+ const response = JSON.parse(result.response);
+
+ return {
+ callWasSuccessful: true,
+ response,
+ };
+ } catch (error) {
+ return {
+ callWasSuccessful: false,
+ error: String(error),
+ };
+ }
+ };
},
});
diff --git a/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts b/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts
index 52824cd86c..1891a779cf 100644
--- a/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts
+++ b/src/common/k8s-api/endpoints/resource-applier.api/request-update.injectable.ts
@@ -3,17 +3,38 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { apiBaseInjectionToken } from "../../api-base";
+import apiBaseInjectable from "../../api-base.injectable";
+import type { AsyncResult } from "../../../utils/async-result";
import type { KubeJsonApiData } from "../../kube-json-api";
-export type RequestKubeObjectCreation = (resourceDescriptor: string) => Promise;
+export type RequestKubeObjectCreation = (resourceDescriptor: string) => Promise>;
const requestKubeObjectCreationInjectable = getInjectable({
id: "request-kube-object-creation",
instantiate: (di): RequestKubeObjectCreation => {
- const apiBase = di.inject(apiBaseInjectionToken);
+ const apiBase = di.inject(apiBaseInjectable);
- return (data) => apiBase.post("/stack", { data });
+ return async (data) => {
+ const result = await apiBase.post("/stack", { data }) as AsyncResult;
+
+ if (!result.callWasSuccessful) {
+ return result;
+ }
+
+ try {
+ const response = JSON.parse(result.response);
+
+ return {
+ callWasSuccessful: true,
+ response,
+ };
+ } catch (error) {
+ return {
+ callWasSuccessful: false,
+ error: String(error),
+ };
+ }
+ };
},
});
diff --git a/src/common/k8s-api/json-api.ts b/src/common/k8s-api/json-api.ts
index c3e07bfa94..988378641c 100644
--- a/src/common/k8s-api/json-api.ts
+++ b/src/common/k8s-api/json-api.ts
@@ -104,7 +104,7 @@ export class JsonApi = Js
);
const { query } = params ?? {};
- if (query) {
+ if (query && Object.keys(query).length > 0) {
const queryString = stringify(query as unknown as QueryParams);
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
@@ -171,7 +171,7 @@ export class JsonApi = Js
reqInit.body = JSON.stringify(data);
}
- if (query) {
+ if (query && Object.keys(query).length > 0) {
const queryString = stringify(query as unknown as QueryParams);
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts
index 7ca2995906..6e3ad4a72a 100644
--- a/src/common/k8s-api/kube-api.ts
+++ b/src/common/k8s-api/kube-api.ts
@@ -403,10 +403,11 @@ export class KubeApi<
/**
* This method differs from {@link formatUrlForNotListing} because this treats `""` as "all namespaces"
+ * NOTE: This is also useful for watching
* @param namespace The namespace to list in or `""` for all namespaces
*/
- formatUrlForListing(namespace: string) {
- return createKubeApiURL({
+ formatUrlForListing(namespace: string | undefined, query?: Partial) {
+ const resourcePath = createKubeApiURL({
apiPrefix: this.apiPrefix,
apiVersion: this.apiVersionWithGroup,
resource: this.apiResource,
@@ -414,15 +415,15 @@ export class KubeApi<
? namespace ?? "default"
: undefined,
});
+
+ return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : "");
}
/**
* Format a URL pathname and query for acting upon a specific resource.
*/
- formatUrlForNotListing(resource?: Partial, query?: Partial): string;
-
- formatUrlForNotListing({ name, namespace }: Partial = {}, query?: Partial) {
- const resourcePath = createKubeApiURL({
+ formatUrlForNotListing({ name, namespace }: Partial = {}) {
+ return createKubeApiURL({
apiPrefix: this.apiPrefix,
apiVersion: this.apiVersionWithGroup,
resource: this.apiResource,
@@ -431,15 +432,17 @@ export class KubeApi<
: undefined,
name,
});
-
- return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : "");
}
/**
- * @deprecated use {@link formatUrlForNotListing} instead
+ * @deprecated use {@link formatUrlForNotListing} or {@link formatUrlForListing} instead
*/
getUrl(resource?: Partial, query?: Partial) {
- return this.formatUrlForNotListing(resource, query);
+ if (query) {
+ return this.formatUrlForListing(resource?.namespace, query);
+ }
+
+ return this.formatUrlForNotListing(resource);
}
protected normalizeQuery(query: Partial = {}) {
@@ -625,14 +628,14 @@ export class KubeApi<
}
getWatchUrl(namespace?: string, query: KubeApiQueryParams = {}) {
- return this.formatUrlForNotListing({ namespace }, {
+ return this.formatUrlForListing(namespace, {
watch: 1,
resourceVersion: this.getResourceVersion(namespace),
...query,
});
}
- watch(opts?: KubeApiWatchOptions): () => void {
+ watch(opts?: KubeApiWatchOptions): Disposer {
let errorReceived = false;
let timedRetry: NodeJS.Timeout;
const {
diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts
index 309c183e42..9e7c541b58 100644
--- a/src/common/k8s-api/kube-object.store.ts
+++ b/src/common/k8s-api/kube-object.store.ts
@@ -3,11 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { ClusterContext } from "./cluster-context";
-
-import { action, computed, makeObservable, observable, reaction, when } from "mobx";
+import { action, computed, makeObservable, observable, reaction } from "mobx";
import type { Disposer } from "../utils";
-import { waitUntilDefined, autoBind, includes, noop, rejectPromiseBy } from "../utils";
+import { waitUntilDefined, autoBind, includes, rejectPromiseBy } from "../utils";
import type { KubeJsonApiDataFor, KubeObject } from "./kube-object";
import { KubeStatus } from "./kube-object";
import type { IKubeWatchEvent } from "./kube-watch-event";
@@ -21,6 +19,7 @@ import assert from "assert";
import type { PartialDeep } from "type-fest";
import { entries } from "../utils/objects";
import AbortController from "abort-controller";
+import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context";
export type OnLoadFailure = (error: unknown) => void;
@@ -85,38 +84,26 @@ export type KubeApiDataFrom = A extends KubeApi = KubeApi>,
D extends KubeJsonApiDataFor = KubeApiDataFrom,
> extends ItemStore {
- static readonly defaultContext = observable.box(); // TODO: support multiple cluster contexts
-
- public readonly api!: A;
public readonly limit: number | undefined;
public readonly bufferSize: number;
- @observable private loadedNamespaces: string[] | undefined = undefined;
- get contextReady() {
- return when(() => Boolean(this.context));
- }
+ private readonly loadedNamespaces = observable.box();
- get namespacesReady() {
- return when(() => Boolean(this.loadedNamespaces));
- }
-
- constructor(api: A, opts?: KubeObjectStoreOptions);
- /**
- * @deprecated Supply API instance through constructor
- */
- constructor();
- constructor(api?: A, opts?: KubeObjectStoreOptions) {
+ constructor(
+ protected readonly dependencies: KubeObjectStoreDependencies,
+ public readonly api: A,
+ opts?: KubeObjectStoreOptions,
+ ) {
super();
-
- if (api) {
- this.api = api;
- }
-
this.limit = opts?.limit;
this.bufferSize = opts?.bufferSize ?? 50_000;
@@ -125,13 +112,9 @@ export abstract class KubeObjectStore<
this.bindWatchEventsUpdater();
}
- get context(): ClusterContext | undefined {
- return KubeObjectStore.defaultContext.get();
- }
-
// TODO: Circular dependency: KubeObjectStore -> ClusterFrameContext -> NamespaceStore -> KubeObjectStore
@computed get contextItems(): K[] {
- const namespaces = this.context?.contextNamespaces ?? [];
+ const namespaces = this.dependencies.context.contextNamespaces;
return this.items.filter(item => {
const itemNamespace = item.getNs();
@@ -202,17 +185,11 @@ export abstract class KubeObjectStore<
}
protected async loadItems({ namespaces, reqInit, onLoadFailure }: KubeObjectStoreLoadingParams): Promise {
- if (!this.context?.cluster?.isAllowedResource(this.api.kind)) {
- return [];
- }
-
- const isLoadingAll = this.context.allNamespaces?.length > 1
- && this.context.cluster.accessibleNamespaces.length === 0
- && this.context.allNamespaces.every(ns => namespaces.includes(ns));
+ const isLoadingAll = this.dependencies.context.isLoadingAll(namespaces);
if (!this.api.isNamespaced || isLoadingAll) {
if (this.api.isNamespaced) {
- this.loadedNamespaces = [];
+ this.loadedNamespaces.set([]);
}
const res = this.api.list({ reqInit }, this.query);
@@ -234,7 +211,7 @@ export abstract class KubeObjectStore<
return await res ?? [];
}
- this.loadedNamespaces = namespaces;
+ this.loadedNamespaces.set(namespaces);
const results = await Promise.allSettled(
namespaces.map(namespace => this.api.list({ namespace, reqInit }, this.query)),
@@ -266,9 +243,7 @@ export abstract class KubeObjectStore<
@action
async loadAll({ namespaces, merge = true, reqInit, onLoadFailure }: KubeObjectStoreLoadAllParams = {}): Promise {
- const context = await waitUntilDefined(() => this.context);
-
- namespaces ??= context.contextNamespaces;
+ namespaces ??= this.dependencies.context.contextNamespaces;
this.isLoading = true;
try {
@@ -425,7 +400,7 @@ export abstract class KubeObjectStore<
}
// collect items from watch-api events to avoid UI blowing up with huge streams of data
- protected eventsBuffer = observable.array>([], { deep: false });
+ protected readonly eventsBuffer = observable.array>([], { deep: false });
protected bindWatchEventsUpdater(delay = 1000) {
reaction(() => [...this.eventsBuffer], this.updateFromEventsBuffer, {
@@ -435,25 +410,24 @@ export abstract class KubeObjectStore<
subscribe({ onLoadFailure, abortController = new AbortController() }: KubeObjectStoreSubscribeParams = {}): Disposer {
if (this.api.isNamespaced) {
- Promise.race([
- rejectPromiseBy(abortController.signal),
- Promise.all([
- waitUntilDefined(() => this.context),
- this.namespacesReady,
- ] as const),
- ])
- .then(([context]) => {
- assert(this.loadedNamespaces);
+ void (async () => {
+ try {
+ const loadedNamespaces = await Promise.race([
+ rejectPromiseBy(abortController.signal),
+ waitUntilDefined(() => this.loadedNamespaces.get()),
+ ]);
- if (context.cluster?.isGlobalWatchEnabled && this.loadedNamespaces.length === 0) {
- return this.watchNamespace("", abortController, { onLoadFailure });
+ if (this.dependencies.context.isGlobalWatchEnabled() && loadedNamespaces.length === 0) {
+ this.watchNamespace("", abortController, { onLoadFailure });
+ } else {
+ for (const namespace of loadedNamespaces) {
+ this.watchNamespace(namespace, abortController, { onLoadFailure });
+ }
}
-
- for (const namespace of this.loadedNamespaces) {
- this.watchNamespace(namespace, abortController, { onLoadFailure });
- }
- })
- .catch(noop); // ignore DOMExceptions
+ } catch (error) {
+ console.error(`[KUBE-OBJECT-STORE]: failed to subscribe to ${this.api.apiBase}`, error);
+ }
+ })();
} else {
this.watchNamespace("", abortController, { onLoadFailure });
}
@@ -467,7 +441,7 @@ export abstract class KubeObjectStore<
}
let timedRetry: NodeJS.Timeout;
- const watch = () => this.api.watch({
+ const startNewWatch = () => this.api.watch({
namespace,
abortController,
callback,
@@ -486,7 +460,7 @@ export abstract class KubeObjectStore<
// not sure what to do, best to retry
clearTimeout(timedRetry);
- timedRetry = setTimeout(watch, 5000);
+ timedRetry = setTimeout(startNewWatch, 5000);
} else if (error instanceof KubeStatus && error.code === 410) {
clearTimeout(timedRetry);
// resourceVersion has gone, let's try to reload
@@ -495,11 +469,11 @@ export abstract class KubeObjectStore<
namespace
? this.loadAll({ namespaces: [namespace], reqInit: { signal }, ...opts })
: this.loadAll({ merge: false, reqInit: { signal }, ...opts })
- ).then(watch);
+ ).then(startNewWatch);
}, 1000);
} else if (error) { // not sure what to do, best to retry
clearTimeout(timedRetry);
- timedRetry = setTimeout(watch, 5000);
+ timedRetry = setTimeout(startNewWatch, 5000);
}
if (data) {
@@ -508,7 +482,7 @@ export abstract class KubeObjectStore<
};
signal.addEventListener("abort", () => clearTimeout(timedRetry));
- watch();
+ startNewWatch();
}
@action
diff --git a/src/common/k8s-api/kube-object.ts b/src/common/k8s-api/kube-object.ts
index 24e34eff4b..53bc6defdf 100644
--- a/src/common/k8s-api/kube-object.ts
+++ b/src/common/k8s-api/kube-object.ts
@@ -645,8 +645,13 @@ export class KubeObject<
}
const requestKubeObjectPatch = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectPatchInjectable);
+ const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
- return requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
+ if (!result.callWasSuccessful) {
+ throw new Error(result.error);
+ }
+
+ return result.response;
}
/**
@@ -665,7 +670,13 @@ export class KubeObject<
...data,
});
- return requestKubeObjectCreation(descriptor);
+ const result = await requestKubeObjectCreation(descriptor);
+
+ if (!result.callWasSuccessful) {
+ throw new Error(result.error);
+ }
+
+ return result.response;
}
/**
diff --git a/src/common/logger.injectable.ts b/src/common/logger.injectable.ts
index e1a085f199..8e9dd2a6a7 100644
--- a/src/common/logger.injectable.ts
+++ b/src/common/logger.injectable.ts
@@ -9,13 +9,23 @@ import { loggerTransportInjectionToken } from "./logger/transports";
const loggerInjectable = getInjectable({
id: "logger",
- instantiate: (di): Logger => createLogger({
- format: format.combine(
- format.splat(),
- format.simple(),
- ),
- transports: di.injectMany(loggerTransportInjectionToken),
- }),
+ instantiate: (di): Logger => {
+ const baseLogger = createLogger({
+ format: format.combine(
+ format.splat(),
+ format.simple(),
+ ),
+ transports: di.injectMany(loggerTransportInjectionToken),
+ });
+
+ return {
+ debug: (message, ...data) => baseLogger.debug(message, ...data),
+ info: (message, ...data) => baseLogger.info(message, ...data),
+ warn: (message, ...data) => baseLogger.warn(message, ...data),
+ error: (message, ...data) => baseLogger.error(message, ...data),
+ silly: (message, ...data) => baseLogger.silly(message, ...data),
+ };
+ },
});
export default loggerInjectable;
diff --git a/src/common/logger.ts b/src/common/logger.ts
index 948404a6b9..0b460a48ff 100644
--- a/src/common/logger.ts
+++ b/src/common/logger.ts
@@ -17,4 +17,6 @@ export interface Logger {
/**
* @deprecated use `di.inject(loggerInjectable)` instead
*/
-export default asLegacyGlobalForExtensionApi(loggerInjectable);
+const logger = asLegacyGlobalForExtensionApi(loggerInjectable);
+
+export default logger;
diff --git a/src/common/rbac.ts b/src/common/rbac.ts
index bfa04ef46b..99e564a377 100644
--- a/src/common/rbac.ts
+++ b/src/common/rbac.ts
@@ -11,51 +11,190 @@ export type KubeResource =
"priorityclasses" | "runtimeclasses" |
"roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts";
-export interface KubeApiResource extends KubeApiResourceData {
- apiName: KubeResource; // valid api resource name (e.g. "namespaces")
+export interface KubeApiResource {
+ kind: string;
+ group: string;
+ apiName: string;
+ namespaced: boolean;
}
+export interface KubeApiResourceDescriptor {
+ apiName: string;
+ group: string;
+}
+
+export const formatKubeApiResource = (res: KubeApiResourceDescriptor) => `${res.group}/${res.apiName}`;
+
export interface KubeApiResourceData {
kind: string; // resource type (e.g. "Namespace")
- group?: string; // api-group
+ group: string; // api-group, if empty then "core"
+ namespaced: boolean;
}
export const apiResourceRecord: Record = {
- "clusterroles": { kind: "ClusterRole", group: "rbac.authorization.k8s.io" },
- "clusterrolebindings": { kind: "ClusterRoleBinding", group: "rbac.authorization.k8s.io" },
- "configmaps": { kind: "ConfigMap" }, //empty group means "core"
- "cronjobs": { kind: "CronJob", group: "batch" },
- "customresourcedefinitions": { kind: "CustomResourceDefinition", group: "apiextensions.k8s.io" },
- "daemonsets": { kind: "DaemonSet", group: "apps" },
- "deployments": { kind: "Deployment", group: "apps" },
- "endpoints": { kind: "Endpoint" },
- "events": { kind: "Event" },
- "horizontalpodautoscalers": { kind: "HorizontalPodAutoscaler", group: "autoscaling" },
- "ingresses": { kind: "Ingress", group: "networking.k8s.io" },
- "jobs": { kind: "Job", group: "batch" },
- "namespaces": { kind: "Namespace" },
- "limitranges": { kind: "LimitRange" },
- "leases": { kind: "Lease" },
- "networkpolicies": { kind: "NetworkPolicy", group: "networking.k8s.io" },
- "nodes": { kind: "Node" },
- "persistentvolumes": { kind: "PersistentVolume" },
- "persistentvolumeclaims": { kind: "PersistentVolumeClaim" },
- "pods": { kind: "Pod" },
- "poddisruptionbudgets": { kind: "PodDisruptionBudget", group: "policy" },
- "podsecuritypolicies": { kind: "PodSecurityPolicy", group: "policy" },
- "priorityclasses": { kind: "PriorityClass", group: "scheduling.k8s.io" },
- "runtimeclasses": { kind: "RuntimeClass", group: "node.k8s.io" },
- "resourcequotas": { kind: "ResourceQuota" },
- "replicasets": { kind: "ReplicaSet", group: "apps" },
- "roles": { kind: "Role", group: "rbac.authorization.k8s.io" },
- "rolebindings": { kind: "RoleBinding", group: "rbac.authorization.k8s.io" },
- "secrets": { kind: "Secret" },
- "serviceaccounts": { kind: "ServiceAccount" },
- "services": { kind: "Service" },
- "statefulsets": { kind: "StatefulSet", group: "apps" },
- "storageclasses": { kind: "StorageClass", group: "storage.k8s.io" },
+ clusterroles: {
+ kind: "ClusterRole",
+ group: "rbac.authorization.k8s.io",
+ namespaced: false,
+ },
+ clusterrolebindings: {
+ kind: "ClusterRoleBinding",
+ group: "rbac.authorization.k8s.io",
+ namespaced: false,
+ },
+ configmaps: {
+ kind: "ConfigMap",
+ group: "v1",
+ namespaced: true,
+ },
+ cronjobs: {
+ kind: "CronJob",
+ group: "batch",
+ namespaced: true,
+ },
+ customresourcedefinitions: {
+ kind: "CustomResourceDefinition",
+ group: "apiextensions.k8s.io",
+ namespaced: false,
+ },
+ daemonsets: {
+ kind: "DaemonSet",
+ group: "apps",
+ namespaced: true,
+ },
+ deployments: {
+ kind: "Deployment",
+ group: "apps",
+ namespaced: true,
+ },
+ endpoints: {
+ kind: "Endpoint",
+ group: "v1",
+ namespaced: true,
+ },
+ events: {
+ kind: "Event",
+ group: "v1",
+ namespaced: true,
+ },
+ horizontalpodautoscalers: {
+ kind: "HorizontalPodAutoscaler",
+ group: "autoscaling",
+ namespaced: true,
+ },
+ ingresses: {
+ kind: "Ingress",
+ group: "networking.k8s.io",
+ namespaced: true,
+ },
+ jobs: {
+ kind: "Job",
+ group: "batch",
+ namespaced: true,
+ },
+ namespaces: {
+ kind: "Namespace",
+ group: "v1",
+ namespaced: false,
+ },
+ limitranges: {
+ kind: "LimitRange",
+ group: "v1",
+ namespaced: true,
+ },
+ leases: {
+ kind: "Lease",
+ group: "v1",
+ namespaced: true,
+ },
+ networkpolicies: {
+ kind: "NetworkPolicy",
+ group: "networking.k8s.io",
+ namespaced: true,
+ },
+ nodes: {
+ kind: "Node",
+ group: "v1",
+ namespaced: false,
+ },
+ persistentvolumes: {
+ kind: "PersistentVolume",
+ group: "v1",
+ namespaced: false,
+ },
+ persistentvolumeclaims: {
+ kind: "PersistentVolumeClaim",
+ group: "v1",
+ namespaced: true,
+ },
+ pods: {
+ kind: "Pod",
+ group: "v1",
+ namespaced: true,
+ },
+ poddisruptionbudgets: {
+ kind: "PodDisruptionBudget",
+ group: "policy",
+ namespaced: true,
+ },
+ podsecuritypolicies: {
+ kind: "PodSecurityPolicy",
+ group: "policy",
+ namespaced: false,
+ },
+ priorityclasses: {
+ kind: "PriorityClass",
+ group: "scheduling.k8s.io",
+ namespaced: false,
+ },
+ runtimeclasses: {
+ kind: "RuntimeClass",
+ group: "node.k8s.io",
+ namespaced: false,
+ },
+ resourcequotas: {
+ kind: "ResourceQuota",
+ group: "v1",
+ namespaced: true,
+ },
+ replicasets: {
+ kind: "ReplicaSet",
+ group: "apps",
+ namespaced: true,
+ },
+ roles: {
+ kind: "Role",
+ group: "rbac.authorization.k8s.io",
+ namespaced: true,
+ },
+ rolebindings: {
+ kind: "RoleBinding",
+ group: "rbac.authorization.k8s.io",
+ namespaced: true,
+ },
+ secrets: {
+ kind: "Secret",
+ group: "v1",
+ namespaced: true,
+ },
+ serviceaccounts: {
+ kind: "ServiceAccount",
+ group: "v1",
+ namespaced: true,
+ },
+ services: {
+ kind: "Service",
+ group: "v1",
+ namespaced: true,
+ },
+ statefulsets: {
+ kind: "StatefulSet",
+ group: "apps",
+ namespaced: true,
+ },
+ storageclasses: {
+ kind: "StorageClass",
+ group: "storage.k8s.io",
+ namespaced: false,
+ },
};
-
-// TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7)
-export const apiResources: KubeApiResource[] = Object.entries(apiResourceRecord)
- .map(([apiName, data]) => ({ apiName: apiName as KubeResource, ...data }));
diff --git a/src/common/utils/computed-or.ts b/src/common/utils/computed-or.ts
new file mode 100644
index 0000000000..4e93394924
--- /dev/null
+++ b/src/common/utils/computed-or.ts
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { IComputedValue } from "mobx";
+import { computed } from "mobx";
+
+export const computedOr = (...values: IComputedValue[]) => computed((
+ () => values.some(value => value.get())
+));
diff --git a/src/common/utils/is-allowed-resource.injectable.ts b/src/common/utils/is-allowed-resource.injectable.ts
deleted file mode 100644
index 8841a8f0cc..0000000000
--- a/src/common/utils/is-allowed-resource.injectable.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
-import { computed } from "mobx";
-import type { KubeResource } from "../rbac";
-import { allowedResourcesInjectionToken } from "../cluster-store/allowed-resources-injection-token";
-
-export type IsAllowedResource = (resource: KubeResource) => boolean;
-
-const isAllowedResourceInjectable = getInjectable({
- id: "is-allowed-resource",
-
- instantiate: (di, resourceName: string) => {
- const allowedResources = di.inject(allowedResourcesInjectionToken);
-
- return computed(() => allowedResources.get().has(resourceName));
- },
-
- lifecycle: lifecycleEnum.keyedSingleton({
- getInstanceKey: (di, resource: string) => resource,
- }),
-});
-
-export default isAllowedResourceInjectable;
diff --git a/src/common/utils/wait.ts b/src/common/utils/wait.ts
index 7bcbd1688b..402d556b5d 100644
--- a/src/common/utils/wait.ts
+++ b/src/common/utils/wait.ts
@@ -8,26 +8,23 @@ import type { Disposer } from "./disposer";
export async function waitUntilDefined(getter: (() => T | null | undefined) | IComputedValue, opts?: { timeout?: number }): Promise {
return new Promise((resolve, reject) => {
- let res: T | null | undefined;
-
when(
() => {
- res = typeof getter === "function"
+ const res = typeof getter === "function"
? getter()
: getter.get();
+ const isDefined = res != null;
- if (res != null) {
+ if (isDefined) {
resolve(res);
-
- return true;
}
- return false;
+ return isDefined;
},
() => {},
{
onError: reject,
- ...opts,
+ ...(opts ?? {}),
},
);
});
diff --git a/src/common/vars.ts b/src/common/vars.ts
index cbde12ff5d..d58e0871e6 100644
--- a/src/common/vars.ts
+++ b/src/common/vars.ts
@@ -6,26 +6,6 @@
// App's common configuration for any process (main, renderer, build pipeline, etc.)
import type { ThemeId } from "../renderer/themes/lens-theme";
-/**
- * @deprecated Switch to using isMacInjectable
- */
-export const isMac = process.platform === "darwin";
-
-/**
- * @deprecated Switch to using isWindowsInjectable
- */
-export const isWindows = process.platform === "win32";
-
-/**
- * @deprecated Switch to using isLinuxInjectable
- */
-export const isLinux = process.platform === "linux";
-
-/**
- * @deprecated switch to using `isDebuggingInjectable`
- */
-export const isDebugging = ["true", "1", "yes", "y", "on"].includes((process.env.DEBUG ?? "").toLowerCase());
-
/**
* @deprecated Switch to using isTestEnvInjectable
*/
diff --git a/src/extensions/common-api/k8s-api.ts b/src/extensions/common-api/k8s-api.ts
index e5c7013bc7..9b62af7551 100644
--- a/src/extensions/common-api/k8s-api.ts
+++ b/src/extensions/common-api/k8s-api.ts
@@ -15,6 +15,12 @@ import type { ResourceApplyingStack } from "../../common/k8s/resource-stack";
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import type { KubernetesCluster } from "./catalog";
+import type { KubeApiDataFrom, KubeObjectStoreOptions } from "../../common/k8s-api/kube-object.store";
+import { KubeObjectStore as InternalKubeObjectStore } from "../../common/k8s-api/kube-object.store";
+import type { KubeJsonApiDataFor, KubeObject } from "../../common/k8s-api/kube-object";
+import type { KubeApi } from "../../common/k8s-api/kube-api";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
+import type { ClusterContext } from "../../renderer/cluster-frame-context/cluster-frame-context";
export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable);
export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable);
@@ -72,8 +78,44 @@ export {
type KubeJsonApiData,
} from "../../common/k8s-api/kube-json-api";
+export abstract class KubeObjectStore<
+ K extends KubeObject = KubeObject,
+ A extends KubeApi = KubeApi>,
+ D extends KubeJsonApiDataFor = KubeApiDataFrom,
+> extends InternalKubeObjectStore {
+ /**
+ * @deprecated This is no longer used and shouldn't have been every really used
+ */
+ static readonly context = {
+ set: (ctx: ClusterContext) => {
+ console.warn("Setting KubeObjectStore.context is no longer supported");
+ void ctx;
+ },
+ get: () => asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable),
+ };
+
+ get context() {
+ return this.dependencies.context;
+ }
+
+ constructor(api: A, opts?: KubeObjectStoreOptions);
+ /**
+ * @deprecated Supply API instance through constructor
+ */
+ constructor();
+ constructor(api?: A, opts?: KubeObjectStoreOptions) {
+ super(
+ {
+ context: asLegacyGlobalForExtensionApi(clusterFrameContextForNamespacedResourcesInjectable),
+ },
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ api!,
+ opts,
+ );
+ }
+}
+
export {
- KubeObjectStore,
type JsonPatch,
type KubeObjectStoreLoadAllParams,
type KubeObjectStoreLoadingParams,
diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts
index 3401d979f7..6335559409 100644
--- a/src/extensions/renderer-api/k8s-api.ts
+++ b/src/extensions/renderer-api/k8s-api.ts
@@ -3,8 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { KubeResource } from "../../common/rbac";
-import isAllowedResourceInjectable from "../../common/utils/is-allowed-resource.injectable";
-import { castArray } from "lodash/fp";
+import { apiResourceRecord } from "../../common/rbac";
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import clusterRoleBindingApiInjectable from "../../common/k8s-api/endpoints/cluster-role-binding.api.injectable";
import clusterRoleApiInjectable from "../../common/k8s-api/endpoints/cluster-role.api.injectable";
@@ -37,13 +36,22 @@ import namespaceApiInjectable from "../../common/k8s-api/endpoints/namespace.api
import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable";
import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable";
import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
+import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
-export function isAllowedResource(resource: KubeResource | KubeResource[]) {
- const resources = castArray(resource);
+export function isAllowedResource(resources: KubeResource | KubeResource[]) {
const di = getLegacyGlobalDiForExtensionApi();
- return resources.every((resourceName: any) => {
- const _isAllowedResource = di.inject(isAllowedResourceInjectable, resourceName);
+ return [resources].flat().every((resourceName) => {
+ const resource = apiResourceRecord[resourceName];
+
+ if (!resource) {
+ return true;
+ }
+
+ const _isAllowedResource = di.inject(shouldShowResourceInjectionToken, {
+ apiName: resourceName,
+ group: resource.group,
+ });
// Note: Legacy isAllowedResource does not advertise reactivity
return _isAllowedResource.get();
diff --git a/src/features/catalog/opening-entity-details.test.tsx b/src/features/catalog/opening-entity-details.test.tsx
index 2d548697ed..8ae49ff548 100644
--- a/src/features/catalog/opening-entity-details.test.tsx
+++ b/src/features/catalog/opening-entity-details.test.tsx
@@ -10,8 +10,8 @@ import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injec
import type { Cluster } from "../../common/cluster/cluster";
import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable";
+import createClusterInjectable from "../../renderer/cluster/create-cluster.injectable";
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
-import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable";
describe("opening catalog entity details panel", () => {
let builder: ApplicationBuilder;
diff --git a/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx b/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx
index c96f2714e1..5c527edbb4 100644
--- a/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx
+++ b/src/features/cluster/delete-dialog/delete-cluster-dialog.test.tsx
@@ -14,7 +14,6 @@ import kubectlBinaryNameInjectable from "../../../main/kubectl/binary-name.injec
import kubectlDownloadingNormalizedArchInjectable from "../../../main/kubectl/normalized-arch.injectable";
import openDeleteClusterDialogInjectable, { type OpenDeleteClusterDialog } from "../../../renderer/components/delete-cluster-dialog/open.injectable";
import { type ApplicationBuilder, getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
-import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
import type { Cluster } from "../../../common/cluster/cluster";
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
@@ -90,7 +89,6 @@ describe("Deleting a cluster", () => {
});
builder.beforeWindowStart((windowDi) => {
- windowDi.override(storesAndApisCanBeCreatedInjectable, () => true);
openDeleteClusterDialog = windowDi.inject(openDeleteClusterDialogInjectable);
});
diff --git a/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx b/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx
index efe0cdf554..f37ada1736 100644
--- a/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx
+++ b/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx
@@ -98,7 +98,10 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
});
});
- builder.allowKubeResource("namespaces");
+ builder.allowKubeResource({
+ apiName: "namespaces",
+ group: "v1",
+ });
});
describe("when navigating to namespaces", () => {
diff --git a/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx b/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx
index a5f834563b..4cf63ee353 100644
--- a/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx
+++ b/src/features/cluster/namespaces/edit-namespace-from-previously-opened-tab.test.tsx
@@ -42,7 +42,10 @@ describe("cluster/namespaces - edit namespaces from previously opened tab", () =
windowDi.override(callForResourceInjectable, () => callForNamespaceMock);
});
- builder.allowKubeResource("namespaces");
+ builder.allowKubeResource({
+ apiName: "namespaces",
+ group: "v1",
+ });
});
describe("given tab was previously opened, when application is started", () => {
diff --git a/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts b/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts
index 93005543db..e4df4c7f8e 100644
--- a/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts
+++ b/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
-import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
import initClusterStoreInjectable from "../../store/renderer/init.injectable";
import requestInitialClusterStatesInjectable from "./request-initial.injectable";
@@ -23,7 +23,7 @@ const setupClusterStateSyncInjectable = getInjectable({
},
runAfter: di.inject(initClusterStoreInjectable),
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default setupClusterStateSyncInjectable;
diff --git a/src/features/cluster/store/renderer/init.injectable.ts b/src/features/cluster/store/renderer/init.injectable.ts
index 2c2795de5c..66d2e31e09 100644
--- a/src/features/cluster/store/renderer/init.injectable.ts
+++ b/src/features/cluster/store/renderer/init.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
-import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
const initClusterStoreInjectable = getInjectable({
@@ -18,7 +18,7 @@ const initClusterStoreInjectable = getInjectable({
},
runAfter: di.inject(initUserStoreInjectable),
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default initClusterStoreInjectable;
diff --git a/src/features/cluster/visibility-of-sidebar-items.test.tsx b/src/features/cluster/visibility-of-sidebar-items.test.tsx
index 0e09f0efa8..2eb42b7fdf 100644
--- a/src/features/cluster/visibility-of-sidebar-items.test.tsx
+++ b/src/features/cluster/visibility-of-sidebar-items.test.tsx
@@ -9,11 +9,11 @@ import { sidebarItemsInjectionToken } from "../../renderer/components/layout/sid
import { computed, runInAction } from "mobx";
import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token";
import React from "react";
-import isAllowedResourceInjectable from "../../common/utils/is-allowed-resource.injectable";
import { frontEndRouteInjectionToken } from "../../common/front-end-routing/front-end-route-injection-token";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
+import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
describe("cluster - visibility of sidebar items", () => {
let builder: ApplicationBuilder;
@@ -50,7 +50,10 @@ describe("cluster - visibility of sidebar items", () => {
describe("when kube resource becomes allowed", () => {
beforeEach(() => {
- builder.allowKubeResource("namespaces");
+ builder.allowKubeResource({
+ apiName: "namespaces",
+ group: "v1",
+ });
});
it("renders", () => {
@@ -69,20 +72,14 @@ describe("cluster - visibility of sidebar items", () => {
const testRouteInjectable = getInjectable({
id: "some-route-injectable-id",
- instantiate: (di) => {
- const someKubeResourceName = "namespaces";
-
- const kubeResourceIsAllowed = di.inject(
- isAllowedResourceInjectable,
- someKubeResourceName,
- );
-
- return {
- path: "/some-child-page",
- isEnabled: kubeResourceIsAllowed,
- clusterFrame: true,
- };
- },
+ instantiate: (di) => ({
+ path: "/some-child-page",
+ clusterFrame: true,
+ isEnabled: di.inject(shouldShowResourceInjectionToken, {
+ apiName: "namespaces",
+ group: "v1",
+ }),
+ }),
injectionToken: frontEndRouteInjectionToken,
});
diff --git a/src/features/cluster/workload-overview.test.tsx b/src/features/cluster/workload-overview.test.tsx
index 1a6750a8f9..205c837b47 100644
--- a/src/features/cluster/workload-overview.test.tsx
+++ b/src/features/cluster/workload-overview.test.tsx
@@ -13,7 +13,10 @@ describe("workload overview", () => {
beforeEach(async () => {
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
- applicationBuilder.allowKubeResource("pods");
+ applicationBuilder.allowKubeResource({
+ apiName: "pods",
+ group: "v1",
+ });
rendered = await applicationBuilder.render();
});
diff --git a/src/features/entity-settings/showing-settings-for-correct-entity.test.tsx b/src/features/entity-settings/showing-settings-for-correct-entity.test.tsx
index 306582a188..36844cb3af 100644
--- a/src/features/entity-settings/showing-settings-for-correct-entity.test.tsx
+++ b/src/features/entity-settings/showing-settings-for-correct-entity.test.tsx
@@ -11,7 +11,7 @@ import type { Cluster } from "../../common/cluster/cluster";
import navigateToEntitySettingsInjectable from "../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable";
import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable";
import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
-import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable";
+import createClusterInjectable from "../../renderer/cluster/create-cluster.injectable";
describe("Showing correct entity settings", () => {
let builder: ApplicationBuilder;
diff --git a/src/features/extensions/navigation-using-application-menu.test.ts b/src/features/extensions/navigation-using-application-menu.test.ts
index c17653d7a3..237e0a93a1 100644
--- a/src/features/extensions/navigation-using-application-menu.test.ts
+++ b/src/features/extensions/navigation-using-application-menu.test.ts
@@ -6,8 +6,6 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
-import downloadBinaryInjectable, { type DownloadBinary } from "../../common/fetch/download-binary.injectable";
-import downloadJsonInjectable, { type DownloadJson } from "../../common/fetch/download-json.injectable";
import focusWindowInjectable from "../../renderer/navigation/focus-window.injectable";
// TODO: Make components free of side effects by making them deterministic
@@ -17,20 +15,14 @@ describe("extensions - navigation using application menu", () => {
let builder: ApplicationBuilder;
let rendered: RenderResult;
let focusWindowMock: jest.Mock;
- let downloadJson: jest.MockedFunction;
- let downloadBinary: jest.MockedFunction;
beforeEach(async () => {
builder = getApplicationBuilder();
builder.beforeWindowStart((windowDi) => {
focusWindowMock = jest.fn();
- downloadJson = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
- downloadBinary = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
windowDi.override(focusWindowInjectable, () => focusWindowMock);
- windowDi.override(downloadJsonInjectable, () => downloadJson);
- windowDi.override(downloadBinaryInjectable, () => downloadBinary);
});
rendered = await builder.render();
diff --git a/src/features/file-system-provisioner/renderer/init-store.injectable.ts b/src/features/file-system-provisioner/renderer/init-store.injectable.ts
index d241dcad38..0e64e49fd6 100644
--- a/src/features/file-system-provisioner/renderer/init-store.injectable.ts
+++ b/src/features/file-system-provisioner/renderer/init-store.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import fileSystemProvisionerStoreInjectable from "../../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable";
-import { beforeFrameStartsInjectionToken } from "../../../renderer/before-frame-starts/tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../../../renderer/before-frame-starts/tokens";
const initFileSystemProvisionerStoreInjectable = getInjectable({
id: "init-file-system-provisioner-store",
@@ -16,7 +16,7 @@ const initFileSystemProvisionerStoreInjectable = getInjectable({
store.load();
},
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default initFileSystemProvisionerStoreInjectable;
diff --git a/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.injectable.ts b/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.injectable.ts
index 64e130aa09..325f3e305d 100644
--- a/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.injectable.ts
+++ b/src/features/helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { sortBy } from "lodash/fp";
-import downloadJsonInjectable from "../../../../../../../common/fetch/download-json.injectable";
+import proxyDownloadJsonInjectable from "../../../../../../../common/fetch/download-json/proxy.injectable";
import { withTimeout } from "../../../../../../../common/fetch/timeout-controller";
import type { HelmRepo } from "../../../../../../../common/helm/helm-repo";
import loggerInjectable from "../../../../../../../common/logger.injectable";
@@ -15,7 +15,7 @@ const requestPublicHelmRepositoriesInjectable = getInjectable({
id: "request-public-helm-repositories",
instantiate: (di) => {
- const downloadJson = di.inject(downloadJsonInjectable);
+ const downloadJson = di.inject(proxyDownloadJsonInjectable);
const logger = di.inject(loggerInjectable);
return async (): Promise => {
diff --git a/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap b/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap
index bdd6779b76..e949e6c3c6 100644
--- a/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap
+++ b/src/features/helm-charts/installing-chart/__snapshots__/opening-dock-tab-for-installing-helm-chart.test.ts.snap
@@ -4631,6 +4631,919 @@ exports[`opening dock tab for installing helm chart given application is started
+
+
+
+
+
+ Chart: some-repository/some-name
+
+
+ content_copy
+
+
+
+ Copy
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-description
+
+ Install
+
+
+
+
+ Version
+
+
+
+
+
+
+
+
+ some-other-version
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Maintainers
+
+
+
+ Some Foo<some@foo.com>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/helm-charts/installing-chart/installing-helm-chart-from-new-tab.test.ts b/src/features/helm-charts/installing-chart/installing-helm-chart-from-new-tab.test.ts
index 9d6c43790d..f7aef8dd88 100644
--- a/src/features/helm-charts/installing-chart/installing-helm-chart-from-new-tab.test.ts
+++ b/src/features/helm-charts/installing-chart/installing-helm-chart-from-new-tab.test.ts
@@ -178,7 +178,10 @@ describe("installing helm chart from new tab", () => {
}),
]);
- await requestHelmChartReadmeMock.resolve("some-readme");
+ await requestHelmChartReadmeMock.resolve({
+ callWasSuccessful: true,
+ response: "some-readme",
+ });
});
it("renders", () => {
@@ -237,9 +240,10 @@ describe("installing helm chart from new tab", () => {
describe("when default configuration and versions resolve", () => {
beforeEach(async () => {
- await requestHelmChartValuesMock.resolve(
- "some-default-configuration",
- );
+ await requestHelmChartValuesMock.resolve({
+ callWasSuccessful: true,
+ response: "some-default-configuration",
+ });
await requestHelmChartVersionsMock.resolve([
HelmChart.create({
@@ -472,7 +476,10 @@ describe("installing helm chart from new tab", () => {
}),
]);
- await requestHelmChartReadmeMock.resolve("some-readme");
+ await requestHelmChartReadmeMock.resolve({
+ callWasSuccessful: true,
+ response: "some-readme",
+ });
});
it("renders", () => {
@@ -531,9 +538,10 @@ describe("installing helm chart from new tab", () => {
describe("when configuration and versions resolve", () => {
beforeEach(async () => {
- await requestHelmChartValuesMock.resolve(
- "some-other-default-configuration",
- );
+ await requestHelmChartValuesMock.resolve({
+ callWasSuccessful: true,
+ response: "some-other-default-configuration",
+ });
await requestHelmChartVersionsMock.resolve([]);
});
@@ -696,9 +704,10 @@ describe("installing helm chart from new tab", () => {
describe("when default configuration resolves", () => {
beforeEach(async () => {
- await requestHelmChartValuesMock.resolve(
- "some-default-configuration-for-other-version",
- );
+ await requestHelmChartValuesMock.resolve({
+ callWasSuccessful: true,
+ response: "some-default-configuration-for-other-version",
+ });
});
it("renders", () => {
@@ -841,9 +850,10 @@ describe("installing helm chart from new tab", () => {
)
.selectOption("some-other-version");
- await requestHelmChartValuesMock.resolve(
- "some-default-configuration-for-other-version",
- );
+ await requestHelmChartValuesMock.resolve({
+ callWasSuccessful: true,
+ response: "some-default-configuration-for-other-version",
+ });
expect(installButton).not.toHaveAttribute("disabled");
});
@@ -914,9 +924,10 @@ describe("installing helm chart from new tab", () => {
)
.selectOption("some-other-version");
- await requestHelmChartValuesMock.resolve(
- "some-default-configuration-for-other-version",
- );
+ await requestHelmChartValuesMock.resolve({
+ callWasSuccessful: true,
+ response: "some-default-configuration-for-other-version",
+ });
const input = rendered.getByTestId(
"monaco-editor-for-some-first-tab-id",
diff --git a/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts b/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts
index 978e1635a7..0b4f749acc 100644
--- a/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts
+++ b/src/features/helm-charts/installing-chart/installing-helm-chart-from-previously-opened-tab.test.ts
@@ -149,9 +149,10 @@ describe("installing helm chart from previously opened tab", () => {
describe("when configuration and version resolves", () => {
beforeEach(async () => {
- await requestHelmChartValuesMock.resolve(
- "some-default-configuration",
- );
+ await requestHelmChartValuesMock.resolve({
+ callWasSuccessful: true,
+ response: "some-default-configuration",
+ });
await requestHelmChartVersionsMock.resolve([
HelmChart.create({
diff --git a/src/features/helm-charts/installing-chart/opening-dock-tab-for-installing-helm-chart.test.ts b/src/features/helm-charts/installing-chart/opening-dock-tab-for-installing-helm-chart.test.ts
index c41a7f14ad..3fd6a7deb7 100644
--- a/src/features/helm-charts/installing-chart/opening-dock-tab-for-installing-helm-chart.test.ts
+++ b/src/features/helm-charts/installing-chart/opening-dock-tab-for-installing-helm-chart.test.ts
@@ -230,7 +230,10 @@ describe("opening dock tab for installing helm chart", () => {
describe("when readme resolves", () => {
beforeEach(async () => {
- await requestHelmChartReadmeMock.resolve("some-readme");
+ await requestHelmChartReadmeMock.resolve({
+ callWasSuccessful: true,
+ response: "some-readme",
+ });
});
it("renders", () => {
@@ -276,7 +279,10 @@ describe("opening dock tab for installing helm chart", () => {
describe("when readme resolves", () => {
beforeEach(async () => {
- await requestHelmChartReadmeMock.resolve("some-readme");
+ await requestHelmChartReadmeMock.resolve({
+ callWasSuccessful: true,
+ response: "some-readme",
+ });
});
it("renders", () => {
@@ -299,6 +305,19 @@ describe("opening dock tab for installing helm chart", () => {
);
});
});
+
+ describe("when readme rejects", () => {
+ beforeEach(async () => {
+ await requestHelmChartReadmeMock.resolve({
+ callWasSuccessful: false,
+ error: "some-error",
+ });
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+ });
});
describe("when selecting to install the chart", () => {
diff --git a/src/features/hotbar/store/renderer/init.injectable.ts b/src/features/hotbar/store/renderer/init.injectable.ts
index 6d341f1ee8..c17e3a4858 100644
--- a/src/features/hotbar/store/renderer/init.injectable.ts
+++ b/src/features/hotbar/store/renderer/init.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
-import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
import initClusterStoreInjectable from "../../../cluster/store/renderer/init.injectable";
const initHotbarStoreInjectable = getInjectable({
@@ -18,7 +18,7 @@ const initHotbarStoreInjectable = getInjectable({
},
runAfter: di.inject(initClusterStoreInjectable),
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default initHotbarStoreInjectable;
diff --git a/src/features/shell-sync/main/compute-unix-shell-environment.injectable.ts b/src/features/shell-sync/main/compute-unix-shell-environment.injectable.ts
index e0f2b2aeb1..fa6de94b57 100644
--- a/src/features/shell-sync/main/compute-unix-shell-environment.injectable.ts
+++ b/src/features/shell-sync/main/compute-unix-shell-environment.injectable.ts
@@ -84,6 +84,7 @@ const computeUnixShellEnvironmentInjectable = getInjectable({
} else if (!cshLikeShellName.test(shellName)) {
// zsh (at least, maybe others) don't load RC files when in non-interactive mode, even when using -l (login) option
shellArgs.push("-i");
+ command = ` ${command}`; // This prevents the command from being added to the history
} else {
// Some shells don't support any other options when providing the -l (login) shell option
}
diff --git a/src/features/shell-sync/main/compute-unix-shell-environment.test.ts b/src/features/shell-sync/main/compute-unix-shell-environment.test.ts
index 55766fbb8a..a540824286 100644
--- a/src/features/shell-sync/main/compute-unix-shell-environment.test.ts
+++ b/src/features/shell-sync/main/compute-unix-shell-environment.test.ts
@@ -218,7 +218,7 @@ describe("computeUnixShellEnvironment technical tests", () => {
});
it("should send the command via stdin", () => {
- expect(stdinValue).toBe(`'/some/process/exec/path' -p '"deadbeef" + JSON.stringify(process.env) + "deadbeef"'`);
+ expect(stdinValue).toBe(` '/some/process/exec/path' -p '"deadbeef" + JSON.stringify(process.env) + "deadbeef"'`);
});
it("should close stdin", () => {
diff --git a/src/features/theme/system-type/renderer/initialize.injectable.ts b/src/features/theme/system-type/renderer/initialize.injectable.ts
index e6ed81f4f1..849c8328b2 100644
--- a/src/features/theme/system-type/renderer/initialize.injectable.ts
+++ b/src/features/theme/system-type/renderer/initialize.injectable.ts
@@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
import systemThemeConfigurationInjectable from "../../../../renderer/themes/system-theme.injectable";
import requestInitialSystemThemeTypeInjectable from "./request-initial.injectable";
@@ -20,7 +20,7 @@ const initializeSystemThemeTypeInjectable = getInjectable({
},
runAfter: di.inject(initUserStoreInjectable),
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default initializeSystemThemeTypeInjectable;
diff --git a/src/jest.setup.ts b/src/jest.setup.ts
index b0acfba878..1e7d0a8ce3 100644
--- a/src/jest.setup.ts
+++ b/src/jest.setup.ts
@@ -3,22 +3,23 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import configurePackages from "./common/configure-packages";
import { configure } from "mobx";
import { setImmediate } from "timers";
import { TextEncoder, TextDecoder as TextDecoderNode } from "util";
import glob from "glob";
import path from "path";
-
-// setup default configuration for external npm-packages
-configurePackages();
+import { enableMapSet, setAutoFreeze } from "immer";
configure({
// Needed because we want to use jest.spyOn()
// ref https://github.com/mobxjs/mobx/issues/2784
safeDescriptors: false,
+ enforceActions: "never",
});
+setAutoFreeze(false); // allow to merge mobx observables
+enableMapSet(); // allow to merge maps and sets
+
// Mock __non_webpack_require__ for tests
globalThis.__non_webpack_require__ = jest.fn();
diff --git a/src/main/__test__/cluster.test.ts b/src/main/__test__/cluster.test.ts
index 4a9b257623..d9e48fb3d9 100644
--- a/src/main/__test__/cluster.test.ts
+++ b/src/main/__test__/cluster.test.ts
@@ -10,7 +10,7 @@ import { getDiForUnitTesting } from "../getDiForUnitTesting";
import type { CreateCluster } from "../../common/cluster/create-cluster-injection-token";
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
-import authorizationNamespaceReviewInjectable from "../../common/cluster/authorization-namespace-review.injectable";
+import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
import type { ClusterContextHandler } from "../context-handler/context-handler";
@@ -20,8 +20,6 @@ import directoryForTempInjectable from "../../common/app-paths/directory-for-tem
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
-import { apiResourceRecord, apiResources } from "../../common/rbac";
-import listApiResourcesInjectable from "../../common/cluster/list-api-resources.injectable";
import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable";
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable";
@@ -46,8 +44,7 @@ describe("create clusters", () => {
di.override(normalizedPlatformInjectable, () => "darwin");
di.override(broadcastMessageInjectable, () => async () => {});
di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true));
- di.override(authorizationNamespaceReviewInjectable, () => () => () => Promise.resolve(Object.keys(apiResourceRecord)));
- di.override(listApiResourcesInjectable, () => () => () => Promise.resolve(apiResources));
+ di.override(requestNamespaceListPermissionsForInjectable, () => () => async () => () => true);
di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
di.override(createContextHandlerInjectable, () => (cluster) => ({
restartServer: jest.fn(),
diff --git a/src/main/cluster/manager.ts b/src/main/cluster/manager.ts
index d5c06c0619..83270941d4 100644
--- a/src/main/cluster/manager.ts
+++ b/src/main/cluster/manager.ts
@@ -210,7 +210,7 @@ export class ClusterManager {
cluster.contextName = entity.spec.kubeconfigContext;
if (entity.spec.accessibleNamespaces) {
- cluster.accessibleNamespaces = entity.spec.accessibleNamespaces;
+ cluster.accessibleNamespaces.replace(entity.spec.accessibleNamespaces);
}
if (entity.spec.metrics) {
diff --git a/src/main/create-cluster/allowed-resources.injectable.ts b/src/main/create-cluster/allowed-resources.injectable.ts
index d614ca3d17..09beae9fd2 100644
--- a/src/main/create-cluster/allowed-resources.injectable.ts
+++ b/src/main/create-cluster/allowed-resources.injectable.ts
@@ -2,15 +2,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 { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import { computed } from "mobx";
-import { allowedResourcesInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
+import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
+import type { KubeApiResourceDescriptor } from "../../common/rbac";
+import { formatKubeApiResource } from "../../common/rbac";
// TODO: Figure out implementation for this later.
const allowedResourcesInjectable = getInjectable({
id: "allowed-resources",
- instantiate: () => computed(() => new Set
()),
- injectionToken: allowedResourcesInjectionToken,
+ instantiate: () => computed(() => false),
+ injectionToken: shouldShowResourceInjectionToken,
+ lifecycle: lifecycleEnum.keyedSingleton({
+ getInstanceKey: (di, resource: KubeApiResourceDescriptor) => formatKubeApiResource(resource),
+ }),
});
export default allowedResourcesInjectable;
diff --git a/src/main/create-cluster/create-cluster.injectable.ts b/src/main/create-cluster/create-cluster.injectable.ts
index e1782ec6c9..f5b302300c 100644
--- a/src/main/create-cluster/create-cluster.injectable.ts
+++ b/src/main/create-cluster/create-cluster.injectable.ts
@@ -11,14 +11,14 @@ import createKubectlInjectable from "../kubectl/create-kubectl.injectable";
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
-import createAuthorizationNamespaceReview from "../../common/cluster/authorization-namespace-review.injectable";
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
-import createListApiResourcesInjectable from "../../common/cluster/list-api-resources.injectable";
+import createListApiResourcesInjectable from "../../common/cluster/request-api-resources.injectable";
import loggerInjectable from "../../common/logger.injectable";
import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable";
import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable";
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
import loadConfigfromFileInjectable from "../../common/kube-helpers/load-config-from-file.injectable";
+import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
const createClusterInjectable = getInjectable({
id: "create-cluster",
@@ -30,8 +30,8 @@ const createClusterInjectable = getInjectable({
createKubectl: di.inject(createKubectlInjectable),
createContextHandler: di.inject(createContextHandlerInjectable),
createAuthorizationReview: di.inject(authorizationReviewInjectable),
- createAuthorizationNamespaceReview: di.inject(createAuthorizationNamespaceReview),
- createListApiResources: di.inject(createListApiResourcesInjectable),
+ requestNamespaceListPermissionsFor: di.inject(requestNamespaceListPermissionsForInjectable),
+ requestApiResources: di.inject(createListApiResourcesInjectable),
createListNamespaces: di.inject(listNamespacesInjectable),
logger: di.inject(loggerInjectable),
detectorRegistry: di.inject(detectorRegistryInjectable),
diff --git a/src/main/electron-app/runnables/setup-runnables-after-window-is-opened.injectable.ts b/src/main/electron-app/runnables/setup-runnables-after-window-is-opened.injectable.ts
index fe501d4750..dc80998de0 100644
--- a/src/main/electron-app/runnables/setup-runnables-after-window-is-opened.injectable.ts
+++ b/src/main/electron-app/runnables/setup-runnables-after-window-is-opened.injectable.ts
@@ -18,7 +18,9 @@ const setupRunnablesAfterWindowIsOpenedInjectable = getInjectable({
return {
id: "setup-runnables-after-window-is-opened",
run: () => {
- app.on("browser-window-created", () => afterWindowIsOpened);
+ app.on("browser-window-created", () => {
+ afterWindowIsOpened();
+ });
return undefined;
},
diff --git a/src/main/k8s/api-base-host-header.injectable.ts b/src/main/k8s/api-base-host-header.injectable.ts
new file mode 100644
index 0000000000..281c4fda84
--- /dev/null
+++ b/src/main/k8s/api-base-host-header.injectable.ts
@@ -0,0 +1,19 @@
+/**
+ * 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 { apiBaseHostHeaderInjectionToken } from "../../common/k8s-api/api-base-configs";
+import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
+
+const apiBaseHostHeaderInjectable = getInjectable({
+ id: "api-base-host-header",
+ instantiate: (di) => {
+ const lensProxyPort = di.inject(lensProxyPortInjectable);
+
+ return `localhost:${lensProxyPort.get()}`;
+ },
+ injectionToken: apiBaseHostHeaderInjectionToken,
+});
+
+export default apiBaseHostHeaderInjectable;
diff --git a/src/main/k8s/api-base-server-address.injectable.ts b/src/main/k8s/api-base-server-address.injectable.ts
new file mode 100644
index 0000000000..ae217cb3e2
--- /dev/null
+++ b/src/main/k8s/api-base-server-address.injectable.ts
@@ -0,0 +1,19 @@
+/**
+ * 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 { apiBaseServerAddressInjectionToken } from "../../common/k8s-api/api-base-configs";
+import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
+
+const apiBaseServerAddressInjectable = getInjectable({
+ id: "api-base-server-address",
+ instantiate: (di) => {
+ const lensProxyPort = di.inject(lensProxyPortInjectable);
+
+ return `http://127.0.0.1:${lensProxyPort.get()}`;
+ },
+ injectionToken: apiBaseServerAddressInjectionToken,
+});
+
+export default apiBaseServerAddressInjectable;
diff --git a/src/main/k8s/api-base.injectable.ts b/src/main/k8s/api-base.injectable.ts
deleted file mode 100644
index 07a9424993..0000000000
--- a/src/main/k8s/api-base.injectable.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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 { apiBaseInjectionToken } from "../../common/k8s-api/api-base";
-import createJsonApiInjectable from "../../common/k8s-api/create-json-api.injectable";
-import { apiPrefix } from "../../common/vars";
-import isDebuggingInjectable from "../../common/vars/is-debugging.injectable";
-import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
-import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
-
-const apiBaseInjectable = getInjectable({
- id: "api-base",
- instantiate: (di) => {
- const proxyPort = di.inject(lensProxyPortInjectable);
- const createJsonApi = di.inject(createJsonApiInjectable);
- const isDebugging = di.inject(isDebuggingInjectable);
-
- return createJsonApi({
- serverAddress: `http://127.0.0.1:${proxyPort.get()}`,
- apiBase: apiPrefix,
- debug: di.inject(isDevelopmentInjectable) || isDebugging,
- }, {
- headers: {
- "Host": `localhost:${proxyPort.get()}`,
- },
- });
- },
- injectionToken: apiBaseInjectionToken,
-});
-
-export default apiBaseInjectable;
diff --git a/src/main/prometheus/operator-provider.injectable.ts.ts b/src/main/prometheus/operator-provider.injectable.ts.ts
index 02891d873b..5215f2dff2 100644
--- a/src/main/prometheus/operator-provider.injectable.ts.ts
+++ b/src/main/prometheus/operator-provider.injectable.ts.ts
@@ -71,23 +71,23 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
case "pods":
switch (queryName) {
case "cpuUsage":
- return `sum(rate(container_cpu_usage_seconds_total{container!="", image!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
+ return `sum(rate(container_cpu_usage_seconds_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
case "cpuRequests":
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "cpuLimits":
return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "memoryUsage":
- return `sum(container_memory_working_set_bytes{container!="", image!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
+ return `sum(container_memory_working_set_bytes{pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "memoryRequests":
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "memoryLimits":
return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "fsUsage":
- return `sum(container_fs_usage_bytes{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
+ return `sum(container_fs_usage_bytes{pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
case "fsWrites":
- return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
+ return `sum(rate(container_fs_writes_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
case "fsReads":
- return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
+ return `sum(rate(container_fs_reads_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
case "networkReceive":
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
case "networkTransmit":
diff --git a/src/main/shell-session/create-shell-session.injectable.ts b/src/main/shell-session/create-shell-session.injectable.ts
index 49ba44cb67..6af65dc908 100644
--- a/src/main/shell-session/create-shell-session.injectable.ts
+++ b/src/main/shell-session/create-shell-session.injectable.ts
@@ -22,10 +22,11 @@ const openShellSessionInjectable = getInjectable({
instantiate: (di): OpenShellSession => {
const openLocalShellSession = di.inject(openLocalShellSessionInjectable);
+ const openNodeShellSession = di.inject(openNodeShellSessionInjectable);
return ({ nodeName, ...args }) => (
nodeName
- ? di.inject(openNodeShellSessionInjectable, { nodeName, ...args })
+ ? openNodeShellSession({ nodeName, ...args })
: openLocalShellSession(args)
);
},
diff --git a/src/main/shell-session/local-shell-session/techincal.test.ts b/src/main/shell-session/local-shell-session/techincal.test.ts
index 37228a6ddc..5c471d923e 100644
--- a/src/main/shell-session/local-shell-session/techincal.test.ts
+++ b/src/main/shell-session/local-shell-session/techincal.test.ts
@@ -4,7 +4,7 @@
*/
import type { DiContainer } from "@ogre-tools/injectable";
-import { WebSocket } from "ws";
+import type WebSocket from "ws";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import type { Cluster } from "../../../common/cluster/cluster";
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
@@ -86,13 +86,18 @@ describe("technical unit tests for local shell sessions", () => {
};
});
+ const websocket = {
+ on: jest.fn(() => websocket),
+ once: jest.fn(() => websocket),
+ } as Partial as WebSocket;
+
await openLocalShellSession({
cluster: {
getProxyKubeconfigPath: async () => "/some-proxy-kubeconfig",
preferences: {},
} as Partial as Cluster,
tabId: "my-tab-id",
- websocket: new WebSocket(null),
+ websocket,
});
});
});
diff --git a/src/main/shell-session/node-shell-session/open.injectable.ts b/src/main/shell-session/node-shell-session/open.injectable.ts
index 739bd7497c..47b97994b7 100644
--- a/src/main/shell-session/node-shell-session/open.injectable.ts
+++ b/src/main/shell-session/node-shell-session/open.injectable.ts
@@ -2,7 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
+import { getInjectable } from "@ogre-tools/injectable";
import type { Cluster } from "../../../common/cluster/cluster";
import type WebSocket from "ws";
import createKubectlInjectable from "../../kubectl/create-kubectl.injectable";
@@ -27,9 +27,11 @@ export interface NodeShellSessionArgs {
nodeName: string;
}
+export type OpenNodeShellSession = (args: NodeShellSessionArgs) => Promise;
+
const openNodeShellSessionInjectable = getInjectable({
id: "open-node-shell-session",
- instantiate: (di, params: NodeShellSessionArgs) => {
+ instantiate: (di): OpenNodeShellSession => {
const createKubectl = di.inject(createKubectlInjectable);
const dependencies: NodeShellSessionDependencies = {
isMac: di.inject(isMacInjectable),
@@ -44,13 +46,14 @@ const openNodeShellSessionInjectable = getInjectable({
emitAppEvent: di.inject(emitAppEventInjectable),
stat: di.inject(statInjectable),
};
- const kubectl = createKubectl(params.cluster.version);
- const session = new NodeShellSession(dependencies, { kubectl, ...params });
- return session.open();
+ return async (args) => {
+ const kubectl = createKubectl(args.cluster.version);
+ const session = new NodeShellSession(dependencies, { kubectl, ...args });
+
+ return session.open();
+ };
},
- // NOTE: this must be transient to bypass the circular dependency of `createKubeJsonApiForClusterInjectable` on the lens proxy port
- lifecycle: lifecycleEnum.transient,
});
export default openNodeShellSessionInjectable;
diff --git a/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts b/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts
index 9d760a8bc2..d2d8f344eb 100644
--- a/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts
+++ b/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts
@@ -5,7 +5,6 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { ContentSource, ElectronWindowTitleBarStyle } from "./create-electron-window.injectable";
import createElectronWindowForInjectable from "./create-electron-window.injectable";
-import assert from "assert";
import type { ClusterFrameInfo } from "../../../../common/cluster-frames";
export interface ElectronWindow {
@@ -69,7 +68,9 @@ const createLensWindowInjectable = getInjectable({
let windowIsStarting = false;
const showWindow = () => {
- assert(browserWindow);
+ if (!browserWindow) {
+ throw new Error("Cannot show browserWindow, does not exist");
+ }
browserWindow.show();
windowIsShown = true;
diff --git a/src/renderer/before-frame-starts/runnables/load-monaco-themes.injectable.ts b/src/renderer/before-frame-starts/runnables/load-monaco-themes.injectable.ts
index 02a00523ad..ef85a84cd0 100644
--- a/src/renderer/before-frame-starts/runnables/load-monaco-themes.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/load-monaco-themes.injectable.ts
@@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import { customMonacoThemeInjectionToken } from "../../components/monaco-editor";
import addNewMonacoThemeInjectable from "../../monaco/add-new-theme.injectable";
-import { beforeFrameStartsInjectionToken } from "../tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../tokens";
const loadMonacoThemesInjectable = getInjectable({
id: "load-monaco-themes",
@@ -18,7 +18,7 @@ const loadMonacoThemesInjectable = getInjectable({
customThemes.forEach(addNewMonacoTheme);
},
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default loadMonacoThemesInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts
index c2c58c22b0..ecfac3136a 100644
--- a/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts
@@ -9,7 +9,9 @@ import { CustomResourceStore } from "../../../common/k8s-api/api-manager/resourc
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
import { KubeApi } from "../../../common/k8s-api/kube-api";
import { KubeObject } from "../../../common/k8s-api/kube-object";
-import { beforeClusterFrameStartsInjectionToken } from "../tokens";
+import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
+import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
const setupAutoRegistrationInjectable = getInjectable({
id: "setup-auto-registration",
@@ -19,6 +21,9 @@ const setupAutoRegistrationInjectable = getInjectable({
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
const beforeApiManagerInitializationApis: KubeApi[] = [];
+ const deps: KubeObjectStoreDependencies = {
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ };
let initialized = false;
const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
@@ -43,7 +48,7 @@ const setupAutoRegistrationInjectable = getInjectable({
})();
if (!apiManager.getStore(api)) {
- apiManager.registerStore(new CustomResourceStore(api));
+ apiManager.registerStore(new CustomResourceStore(deps, api));
}
};
const autoInitKubeApi = (api: KubeApi) => {
@@ -66,6 +71,7 @@ const setupAutoRegistrationInjectable = getInjectable({
}
});
+ // NOTE: this MUST happen after the event emitter listeners are registered
const apiManager = di.inject(apiManagerInjectable);
beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
@@ -73,7 +79,7 @@ const setupAutoRegistrationInjectable = getInjectable({
initialized = true;
},
}),
- injectionToken: beforeClusterFrameStartsInjectionToken,
+ injectionToken: beforeClusterFrameStartsSecondInjectionToken,
});
export default setupAutoRegistrationInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-current-cluster-broadcast.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-current-cluster-broadcast.injectable.ts
index 978eae3d95..a89ba58ec2 100644
--- a/src/renderer/before-frame-starts/runnables/setup-current-cluster-broadcast.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/setup-current-cluster-broadcast.injectable.ts
@@ -7,7 +7,7 @@ import { reaction } from "mobx";
import { currentClusterMessageChannel } from "../../../common/cluster/current-cluster-channel";
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
import matchedClusterIdInjectable from "../../navigation/matched-cluster-id.injectable";
-import { beforeMainFrameStartsInjectionToken } from "../tokens";
+import { beforeMainFrameStartsFirstInjectionToken } from "../tokens";
const setupCurrentClusterBroadcastInjectable = getInjectable({
id: "setup-current-cluster-broadcast",
@@ -26,7 +26,7 @@ const setupCurrentClusterBroadcastInjectable = getInjectable({
);
},
}),
- injectionToken: beforeMainFrameStartsInjectionToken,
+ injectionToken: beforeMainFrameStartsFirstInjectionToken,
});
export default setupCurrentClusterBroadcastInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts
index 406b1641af..20566b5dac 100644
--- a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts
@@ -9,7 +9,7 @@ import isLinuxInjectable from "../../../common/vars/is-linux.injectable";
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import openPathPickingDialogInjectable from "../../../features/path-picking-dialog/renderer/pick-paths.injectable";
import addSyncEntriesInjectable from "../../initializers/add-sync-entries.injectable";
-import { beforeFrameStartsInjectionToken } from "../tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../tokens";
const setupKubernetesClusterCatalogAddMenuListenerInjectable = getInjectable({
id: "setup-kubernetes-cluster-catalog-add-menu-listener",
@@ -75,7 +75,7 @@ const setupKubernetesClusterCatalogAddMenuListenerInjectable = getInjectable({
});
},
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default setupKubernetesClusterCatalogAddMenuListenerInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts
index 150620c22a..73cc988e2c 100644
--- a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts
@@ -9,7 +9,7 @@ import readFileInjectable from "../../../common/fs/read-file.injectable";
import { loadConfigFromString } from "../../../common/kube-helpers";
import loggerInjectable from "../../../common/logger.injectable";
import openDeleteClusterDialogInjectable from "../../components/delete-cluster-dialog/open.injectable";
-import { beforeFrameStartsInjectionToken } from "../tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../tokens";
const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
id: "setup-kubernetes-cluster-context-menu-open",
@@ -50,7 +50,7 @@ const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
});
},
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default setupKubernetesClusterContextMenuOpenInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-root-mac-class.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-root-mac-class.injectable.ts
index 59a684ccc9..56b9ce66fa 100644
--- a/src/renderer/before-frame-starts/runnables/setup-root-mac-class.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/setup-root-mac-class.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import isMacInjectable from "../../../common/vars/is-mac.injectable";
-import { beforeFrameStartsInjectionToken } from "../tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../tokens";
const setupRootMacClassnameInjectable = getInjectable({
id: "setup-root-mac-classname",
@@ -17,7 +17,7 @@ const setupRootMacClassnameInjectable = getInjectable({
rootElem?.classList.toggle("is-mac", isMac);
},
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default setupRootMacClassnameInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-sentry.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-sentry.injectable.ts
index 401cec99d5..c6af3b7614 100644
--- a/src/renderer/before-frame-starts/runnables/setup-sentry.injectable.ts
+++ b/src/renderer/before-frame-starts/runnables/setup-sentry.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import initializeSentryReportingWithInjectable from "../../../common/error-reporting/initialize-sentry-reporting.injectable";
-import { beforeMainFrameStartsInjectionToken } from "../tokens";
+import { beforeMainFrameStartsFirstInjectionToken } from "../tokens";
import { init } from "@sentry/electron/renderer";
const setupSentryInjectable = getInjectable({
@@ -17,7 +17,7 @@ const setupSentryInjectable = getInjectable({
initializeSentryReportingWith(init);
},
}),
- injectionToken: beforeMainFrameStartsInjectionToken,
+ injectionToken: beforeMainFrameStartsFirstInjectionToken,
});
export default setupSentryInjectable;
diff --git a/src/renderer/before-frame-starts/runnables/setup-weblink-context-menu-open.injectable.tsx b/src/renderer/before-frame-starts/runnables/setup-weblink-context-menu-open.injectable.tsx
index 4c027d6047..7bf8f29422 100644
--- a/src/renderer/before-frame-starts/runnables/setup-weblink-context-menu-open.injectable.tsx
+++ b/src/renderer/before-frame-starts/runnables/setup-weblink-context-menu-open.injectable.tsx
@@ -7,7 +7,7 @@ import React from "react";
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
import { WeblinkAddCommand } from "../../components/catalog-entities/weblink-add-command";
import commandOverlayInjectable from "../../components/command-palette/command-overlay.injectable";
-import { beforeFrameStartsInjectionToken } from "../tokens";
+import { beforeFrameStartsSecondInjectionToken } from "../tokens";
const setupWeblickContextMenuOpenInjectable = getInjectable({
id: "setup-weblick-context-menu-open",
@@ -28,7 +28,7 @@ const setupWeblickContextMenuOpenInjectable = getInjectable({
});
},
}),
- injectionToken: beforeFrameStartsInjectionToken,
+ injectionToken: beforeFrameStartsSecondInjectionToken,
});
export default setupWeblickContextMenuOpenInjectable;
diff --git a/src/renderer/before-frame-starts/tokens.ts b/src/renderer/before-frame-starts/tokens.ts
index 77b0350315..460c44cde9 100644
--- a/src/renderer/before-frame-starts/tokens.ts
+++ b/src/renderer/before-frame-starts/tokens.ts
@@ -7,20 +7,30 @@ import type { Runnable } from "../../common/runnable/run-many-for";
// NOTE: these are run before any other token, mostly to set up things that all other runnables need
export const beforeFrameStartsFirstInjectionToken = getInjectionToken({
- id: "even-before-frame-starts",
+ id: "before-frame-starts-first",
});
// NOTE: these are only run when process.isMainFrame === true
-export const beforeMainFrameStartsInjectionToken = getInjectionToken({
- id: "even-before-main-frame-starts",
+export const beforeMainFrameStartsFirstInjectionToken = getInjectionToken({
+ id: "before-main-frame-starts-first",
});
// NOTE: these are only run when process.isMainFrame === false
-export const beforeClusterFrameStartsInjectionToken = getInjectionToken({
- id: "even-before-cluster-frame-starts",
+export const beforeClusterFrameStartsFirstInjectionToken = getInjectionToken({
+ id: "before-cluster-frame-starts-first",
});
-export const beforeFrameStartsInjectionToken = getInjectionToken({
- id: "before-frame-starts",
+export const beforeFrameStartsSecondInjectionToken = getInjectionToken({
+ id: "before-frame-starts-second",
+});
+
+// NOTE: these are only run when process.isMainFrame === true
+export const beforeMainFrameStartsSecondInjectionToken = getInjectionToken({
+ id: "before-main-frame-starts-second",
+});
+
+// NOTE: these are only run when process.isMainFrame === false
+export const beforeClusterFrameStartsSecondInjectionToken = getInjectionToken({
+ id: "before-cluster-frame-starts-second",
});
diff --git a/src/renderer/cluster-frame-context/allowed-resources.injectable.ts b/src/renderer/cluster-frame-context/allowed-resources.injectable.ts
deleted file mode 100644
index 646d931367..0000000000
--- a/src/renderer/cluster-frame-context/allowed-resources.injectable.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * 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 { comparer, computed } from "mobx";
-import hostedClusterInjectable from "./hosted-cluster.injectable";
-import { allowedResourcesInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
-
-const allowedResourcesInjectable = getInjectable({
- id: "allowed-resources",
-
- instantiate: (di) => {
- const cluster = di.inject(hostedClusterInjectable);
-
- return computed(() => new Set(cluster?.allowedResources), {
- // This needs to be here so that during refresh changes are only propogated when necessary
- equals: (cur, prev) => comparer.structural(cur, prev),
- });
- },
-
- injectionToken: allowedResourcesInjectionToken,
-});
-
-export default allowedResourcesInjectable;
diff --git a/src/renderer/cluster-frame-context/cluster-frame-context.injectable.ts b/src/renderer/cluster-frame-context/cluster-frame-context.injectable.ts
deleted file mode 100644
index a353a30b40..0000000000
--- a/src/renderer/cluster-frame-context/cluster-frame-context.injectable.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * 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 { ClusterFrameContext } from "./cluster-frame-context";
-import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
-import hostedClusterInjectable from "./hosted-cluster.injectable";
-import assert from "assert";
-
-const clusterFrameContextInjectable = getInjectable({
- id: "cluster-frame-context",
-
- instantiate: (di) => {
- const cluster = di.inject(hostedClusterInjectable);
-
- assert(cluster, "This can only be injected within a cluster frame");
-
- return new ClusterFrameContext(cluster, {
- namespaceStore: di.inject(namespaceStoreInjectable),
- });
- },
-});
-
-export default clusterFrameContextInjectable;
diff --git a/src/renderer/cluster-frame-context/cluster-frame-context.ts b/src/renderer/cluster-frame-context/cluster-frame-context.ts
index 1f1625e9e1..e23bac5fd4 100755
--- a/src/renderer/cluster-frame-context/cluster-frame-context.ts
+++ b/src/renderer/cluster-frame-context/cluster-frame-context.ts
@@ -3,44 +3,14 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { Cluster } from "../../common/cluster/cluster";
-import type { NamespaceStore } from "../components/+namespaces/store";
-import type { ClusterContext } from "../../common/k8s-api/cluster-context";
-import { computed, makeObservable } from "mobx";
+/**
+ * This type is used for KubeObjectStores
+ */
+export interface ClusterContext {
+ readonly allNamespaces: string[]; // available / allowed namespaces from cluster.ts
+ readonly contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
+ readonly hasSelectedAll: boolean;
-interface Dependencies {
- namespaceStore: NamespaceStore;
-}
-
-export class ClusterFrameContext implements ClusterContext {
- constructor(public cluster: Cluster, private dependencies: Dependencies) {
- makeObservable(this);
- }
-
- @computed get allNamespaces(): string[] {
- // user given list of namespaces
- if (this.cluster.accessibleNamespaces.length) {
- return this.cluster.accessibleNamespaces;
- }
-
- if (this.dependencies.namespaceStore.items.length > 0) {
- // namespaces from kubernetes api
- return this.dependencies.namespaceStore.items.map((namespace) => namespace.getName());
- } else {
- // fallback to cluster resolved namespaces because we could not load list
- return this.cluster.allowedNamespaces || [];
- }
- }
-
- @computed get contextNamespaces(): string[] {
- return this.dependencies.namespaceStore.contextNamespaces;
- }
-
- @computed get hasSelectedAll(): boolean {
- const namespaces = new Set(this.contextNamespaces);
-
- return this.allNamespaces?.length > 1
- && this.cluster.accessibleNamespaces.length === 0
- && this.allNamespaces.every(ns => namespaces.has(ns));
- }
+ isLoadingAll(namespaces: string[]): boolean;
+ isGlobalWatchEnabled(): boolean;
}
diff --git a/src/renderer/cluster-frame-context/for-cluster-scoped-resources.injectable.ts b/src/renderer/cluster-frame-context/for-cluster-scoped-resources.injectable.ts
new file mode 100644
index 0000000000..12e74cdc1f
--- /dev/null
+++ b/src/renderer/cluster-frame-context/for-cluster-scoped-resources.injectable.ts
@@ -0,0 +1,21 @@
+/**
+ * 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 type { ClusterContext } from "./cluster-frame-context";
+
+const clusterFrameContextForClusterScopedResourcesInjectable = getInjectable({
+ id: "cluster-frame-context-for-cluster-scoped-resources",
+ instantiate: (): ClusterContext => ({
+ // This doesn't matter as it is an optimization for namespaced resources only
+ isGlobalWatchEnabled: () => true,
+ // This is always the case for cluster scoped resources
+ isLoadingAll: () => true,
+ allNamespaces: [],
+ contextNamespaces: [],
+ hasSelectedAll: true,
+ }),
+});
+
+export default clusterFrameContextForClusterScopedResourcesInjectable;
diff --git a/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts b/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts
new file mode 100644
index 0000000000..1eb85b3a43
--- /dev/null
+++ b/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts
@@ -0,0 +1,64 @@
+/**
+ * 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 type { ClusterContext } from "./cluster-frame-context";
+import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
+import hostedClusterInjectable from "./hosted-cluster.injectable";
+import assert from "assert";
+import { computed } from "mobx";
+
+const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
+ id: "cluster-frame-context-for-namespaced-resources",
+
+ instantiate: (di): ClusterContext => {
+ const cluster = di.inject(hostedClusterInjectable);
+ const namespaceStore = di.inject(namespaceStoreInjectable);
+
+ assert(cluster, "This can only be injected within a cluster frame");
+
+ const allNamespaces = computed(() => {
+ // user given list of namespaces
+ if (cluster.accessibleNamespaces.length) {
+ return cluster.accessibleNamespaces.slice();
+ }
+
+ if (namespaceStore.items.length > 0) {
+ // namespaces from kubernetes api
+ return namespaceStore.items.map((namespace) => namespace.getName());
+ }
+
+ // fallback to cluster resolved namespaces because we could not load list
+ return cluster.allowedNamespaces.slice();
+ });
+ const contextNamespaces = computed(() => namespaceStore.contextNamespaces);
+ const hasSelectedAll = computed(() => {
+ const namespaces = new Set(contextNamespaces.get());
+
+ return allNamespaces.get().length > 1
+ && cluster.accessibleNamespaces.length === 0
+ && allNamespaces.get().every(ns => namespaces.has(ns));
+ });
+
+ return {
+ isLoadingAll: (namespaces) => (
+ allNamespaces.get().length > 1
+ && cluster.accessibleNamespaces.length === 0
+ && allNamespaces.get().every(ns => namespaces.includes(ns))
+ ),
+ isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled,
+ get allNamespaces() {
+ return allNamespaces.get();
+ },
+ get contextNamespaces() {
+ return contextNamespaces.get();
+ },
+ get hasSelectedAll() {
+ return hasSelectedAll.get();
+ },
+ };
+ },
+});
+
+export default clusterFrameContextForNamespacedResourcesInjectable;
diff --git a/src/renderer/cluster-frame-context/should-show-resource.injectable.ts b/src/renderer/cluster-frame-context/should-show-resource.injectable.ts
new file mode 100644
index 0000000000..2ec4132045
--- /dev/null
+++ b/src/renderer/cluster-frame-context/should-show-resource.injectable.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
+import { computed } from "mobx";
+import hostedClusterInjectable from "./hosted-cluster.injectable";
+import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
+import type { KubeApiResourceDescriptor } from "../../common/rbac";
+import { formatKubeApiResource } from "../../common/rbac";
+
+const shouldShowResourceInjectable = getInjectable({
+ id: "should-show-resource",
+ instantiate: (di, resource) => {
+ const cluster = di.inject(hostedClusterInjectable);
+
+ return cluster
+ ? computed(() => cluster.shouldShowResource(resource))
+ : computed(() => false);
+ },
+ injectionToken: shouldShowResourceInjectionToken,
+ lifecycle: lifecycleEnum.keyedSingleton({
+ getInstanceKey: (di, resource: KubeApiResourceDescriptor) => formatKubeApiResource(resource),
+ }),
+});
+
+export default shouldShowResourceInjectable;
diff --git a/src/renderer/cluster/accessible-namespaces.injectable.ts b/src/renderer/cluster/accessible-namespaces.injectable.ts
new file mode 100644
index 0000000000..50743cfa83
--- /dev/null
+++ b/src/renderer/cluster/accessible-namespaces.injectable.ts
@@ -0,0 +1,18 @@
+/**
+ * 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 { computed } from "mobx";
+import hostedClusterInjectable from "../cluster-frame-context/hosted-cluster.injectable";
+
+const clusterConfiguredAccessibleNamespacesInjectable = getInjectable({
+ id: "cluster-configured-accessible-namespaces",
+ instantiate: (di) => {
+ const hostedCluster = di.inject(hostedClusterInjectable);
+
+ return computed(() => [...hostedCluster?.accessibleNamespaces ?? []]);
+ },
+});
+
+export default clusterConfiguredAccessibleNamespacesInjectable;
diff --git a/src/renderer/create-cluster/create-cluster.injectable.ts b/src/renderer/cluster/create-cluster.injectable.ts
similarity index 92%
rename from src/renderer/create-cluster/create-cluster.injectable.ts
rename to src/renderer/cluster/create-cluster.injectable.ts
index e0a9f51656..385dfe8d66 100644
--- a/src/renderer/create-cluster/create-cluster.injectable.ts
+++ b/src/renderer/cluster/create-cluster.injectable.ts
@@ -27,9 +27,9 @@ const createClusterInjectable = getInjectable({
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
createContextHandler: () => undefined as never,
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
- createAuthorizationNamespaceReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
+ requestNamespaceListPermissionsFor: () => { throw new Error("Tried to access back-end feature in front-end."); },
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
- createListApiResources: ()=> { throw new Error("Tried to access back-end feature in front-end."); },
+ requestApiResources: ()=> { throw new Error("Tried to access back-end feature in front-end."); },
detectorRegistry: undefined as never,
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
};
diff --git a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts
index 6389009785..a4666adcef 100644
--- a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts
+++ b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.injectable.ts
@@ -13,6 +13,7 @@ import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-cre
import assert from "assert";
import nodeStoreInjectable from "../../+nodes/store.injectable";
import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
const clusterOverviewStoreInjectable = getInjectable({
id: "cluster-overview-store",
@@ -32,6 +33,7 @@ const clusterOverviewStoreInjectable = getInjectable({
),
nodeStore: di.inject(nodeStoreInjectable),
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
}, clusterApi);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts
index 2669cb71a6..943b14dbf9 100644
--- a/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts
+++ b/src/renderer/components/+cluster/cluster-overview-store/cluster-overview-store.ts
@@ -4,6 +4,7 @@
*/
import { action, observable, reaction, when, makeObservable } from "mobx";
+import type { KubeObjectStoreDependencies } from "../../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store";
import type { Cluster, ClusterApi } from "../../../../common/k8s-api/endpoints";
import type { StorageLayer } from "../../../utils";
@@ -28,7 +29,7 @@ export interface ClusterOverviewStorageState {
metricNodeRole: MetricNodeRole;
}
-interface ClusterOverviewStoreDependencies {
+interface ClusterOverviewStoreDependencies extends KubeObjectStoreDependencies {
readonly storage: StorageLayer;
readonly nodeStore: NodeStore;
requestClusterMetricsByNodeNames: RequestClusterMetricsByNodeNames;
@@ -58,7 +59,7 @@ export class ClusterOverviewStore extends KubeObjectStore i
}
constructor(protected readonly dependencies: ClusterOverviewStoreDependencies, api: ClusterApi) {
- super(api);
+ super(dependencies, api);
makeObservable(this);
autoBind(this);
diff --git a/src/renderer/components/+config-autoscalers/store.injectable.ts b/src/renderer/components/+config-autoscalers/store.injectable.ts
index a2d271df5d..4d4a3adc21 100644
--- a/src/renderer/components/+config-autoscalers/store.injectable.ts
+++ b/src/renderer/components/+config-autoscalers/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import horizontalPodAutoscalerApiInjectable from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { HorizontalPodAutoscalerStore } from "./store";
@@ -16,7 +17,9 @@ const horizontalPodAutoscalerStoreInjectable = getInjectable({
const api = di.inject(horizontalPodAutoscalerApiInjectable);
- return new HorizontalPodAutoscalerStore(api);
+ return new HorizontalPodAutoscalerStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-leases/store.injectable.ts b/src/renderer/components/+config-leases/store.injectable.ts
index b9dda24b07..eb9676ce90 100644
--- a/src/renderer/components/+config-leases/store.injectable.ts
+++ b/src/renderer/components/+config-leases/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import leaseApiInjectable from "../../../common/k8s-api/endpoints/lease.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { LeaseStore } from "./store";
@@ -16,7 +17,9 @@ const leaseStoreInjectable = getInjectable({
const api = di.inject(leaseApiInjectable);
- return new LeaseStore(api);
+ return new LeaseStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-limit-ranges/store.injectable.ts b/src/renderer/components/+config-limit-ranges/store.injectable.ts
index 4f1224131a..494968b60c 100644
--- a/src/renderer/components/+config-limit-ranges/store.injectable.ts
+++ b/src/renderer/components/+config-limit-ranges/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import limitRangeApiInjectable from "../../../common/k8s-api/endpoints/limit-range.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { LimitRangeStore } from "./store";
@@ -16,7 +17,9 @@ const limitRangeStoreInjectable = getInjectable({
const api = di.inject(limitRangeApiInjectable);
- return new LimitRangeStore(api);
+ return new LimitRangeStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-maps/store.injectable.ts b/src/renderer/components/+config-maps/store.injectable.ts
index d16da8318e..7cbd59e4a5 100644
--- a/src/renderer/components/+config-maps/store.injectable.ts
+++ b/src/renderer/components/+config-maps/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import configMapApiInjectable from "../../../common/k8s-api/endpoints/config-map.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { ConfigMapStore } from "./store";
@@ -16,7 +17,9 @@ const configMapStoreInjectable = getInjectable({
const api = di.inject(configMapApiInjectable);
- return new ConfigMapStore(api);
+ return new ConfigMapStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts
index 5314584274..697fd5c444 100644
--- a/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts
+++ b/src/renderer/components/+config-pod-disruption-budgets/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import podDisruptionBudgetApiInjectable from "../../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PodDisruptionBudgetStore } from "./store";
@@ -16,7 +17,9 @@ const podDisruptionBudgetStoreInjectable = getInjectable({
const api = di.inject(podDisruptionBudgetApiInjectable);
- return new PodDisruptionBudgetStore(api);
+ return new PodDisruptionBudgetStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-priority-classes/store.injectable.ts b/src/renderer/components/+config-priority-classes/store.injectable.ts
index 68677a3b5f..b01e69602c 100644
--- a/src/renderer/components/+config-priority-classes/store.injectable.ts
+++ b/src/renderer/components/+config-priority-classes/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PriorityClassStore } from "./store";
@@ -16,7 +17,9 @@ const priorityClassStoreInjectable = getInjectable({
const api = di.inject(priorityClassApiInjectable);
- return new PriorityClassStore(api);
+ return new PriorityClassStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-resource-quotas/store.injectable.ts b/src/renderer/components/+config-resource-quotas/store.injectable.ts
index d8294c8132..4999ca9517 100644
--- a/src/renderer/components/+config-resource-quotas/store.injectable.ts
+++ b/src/renderer/components/+config-resource-quotas/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import resourceQuotaApiInjectable from "../../../common/k8s-api/endpoints/resource-quota.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { ResourceQuotaStore } from "./store";
@@ -16,7 +17,9 @@ const resourceQuotaStoreInjectable = getInjectable({
const api = di.inject(resourceQuotaApiInjectable);
- return new ResourceQuotaStore(api);
+ return new ResourceQuotaStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-runtime-classes/store.injectable.ts b/src/renderer/components/+config-runtime-classes/store.injectable.ts
index 0a4a21d716..63e8d82526 100644
--- a/src/renderer/components/+config-runtime-classes/store.injectable.ts
+++ b/src/renderer/components/+config-runtime-classes/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { RuntimeClassStore } from "./store";
@@ -16,7 +17,9 @@ const runtimeClassStoreInjectable = getInjectable({
const api = di.inject(runtimeClassApiInjectable);
- return new RuntimeClassStore(api);
+ return new RuntimeClassStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx b/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx
index f1e3aad2b4..6a586bdd0c 100644
--- a/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx
+++ b/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx
@@ -9,6 +9,10 @@ import { Secret, SecretType } from "../../../../common/k8s-api/endpoints";
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
import { renderFor } from "../../test-utils/renderFor";
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 hostedClusterInjectable from "../../../cluster-frame-context/hosted-cluster.injectable";
+import createClusterInjectable from "../../../cluster/create-cluster.injectable";
+import directoryForKubeConfigsInjectable from "../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
jest.mock("../../kube-object-meta/kube-object-meta", () => ({
KubeObjectMeta: () => null,
@@ -19,8 +23,20 @@ describe("SecretDetails tests", () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
const render = renderFor(di);
+ di.override(directoryForUserDataInjectable, () => "/some-user-data");
+ 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",
+ }));
+
const secret = new Secret({
apiVersion: "v1",
kind: "secret",
diff --git a/src/renderer/components/+config-secrets/store.injectable.ts b/src/renderer/components/+config-secrets/store.injectable.ts
index 9e84f20696..2af7bf13f6 100644
--- a/src/renderer/components/+config-secrets/store.injectable.ts
+++ b/src/renderer/components/+config-secrets/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { SecretStore } from "./store";
@@ -16,7 +17,9 @@ const secretStoreInjectable = getInjectable({
const api = di.inject(secretApiInjectable);
- return new SecretStore(api);
+ return new SecretStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+custom-resources/definition.store.injectable.ts b/src/renderer/components/+custom-resources/definition.store.injectable.ts
index ee4719cc97..b93f656df9 100644
--- a/src/renderer/components/+custom-resources/definition.store.injectable.ts
+++ b/src/renderer/components/+custom-resources/definition.store.injectable.ts
@@ -7,6 +7,7 @@ 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 customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { CustomResourceDefinitionStore } from "./definition.store";
@@ -19,6 +20,7 @@ const customResourceDefinitionStoreInjectable = getInjectable({
return new CustomResourceDefinitionStore({
autoRegistration: di.inject(autoRegistrationEmitterInjectable),
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+custom-resources/definition.store.ts b/src/renderer/components/+custom-resources/definition.store.ts
index 778f895214..bef5c2323e 100644
--- a/src/renderer/components/+custom-resources/definition.store.ts
+++ b/src/renderer/components/+custom-resources/definition.store.ts
@@ -4,7 +4,7 @@
*/
import { computed, reaction, makeObservable } from "mobx";
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import { autoBind } from "../../utils";
import type { CustomResourceDefinition, CustomResourceDefinitionApi } from "../../../common/k8s-api/endpoints/custom-resource-definition.api";
@@ -12,7 +12,7 @@ import type { KubeObject } from "../../../common/k8s-api/kube-object";
import type TypedEventEmitter from "typed-emitter";
import type { LegacyAutoRegistration } from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
-export interface CustomResourceDefinitionStoreDependencies {
+export interface CustomResourceDefinitionStoreDependencies extends KubeObjectStoreDependencies {
readonly autoRegistration: TypedEventEmitter;
}
@@ -22,7 +22,7 @@ export class CustomResourceDefinitionStore extends KubeObjectStore {
constructor(
- protected readonly dependencies: Dependencies,
+ protected readonly dependencies: EventStoreDependencies,
api: KubeEventApi,
opts: KubeObjectStoreOptions = {},
) {
- super(api, { limit: 1000, ...opts });
+ super(dependencies, api, { limit: 1000, ...opts });
autoBind(this);
}
diff --git a/src/renderer/components/+extensions/__tests__/extensions.test.tsx b/src/renderer/components/+extensions/__tests__/extensions.test.tsx
index b17b6bc782..751c1a38cc 100644
--- a/src/renderer/components/+extensions/__tests__/extensions.test.tsx
+++ b/src/renderer/components/+extensions/__tests__/extensions.test.tsx
@@ -25,9 +25,7 @@ import extensionInstallationStateStoreInjectable from "../../../../extensions/ex
import { observable, when } from "mobx";
import type { RemovePath } from "../../../../common/fs/remove.injectable";
import removePathInjectable from "../../../../common/fs/remove.injectable";
-import type { DownloadJson } from "../../../../common/fetch/download-json.injectable";
import type { DownloadBinary } from "../../../../common/fetch/download-binary.injectable";
-import downloadJsonInjectable from "../../../../common/fetch/download-json.injectable";
import downloadBinaryInjectable from "../../../../common/fetch/download-binary.injectable";
import currentlyInClusterFrameInjectable from "../../../routes/currently-in-cluster-frame.injectable";
@@ -38,7 +36,6 @@ describe("Extensions", () => {
let extensionInstallationStateStore: ExtensionInstallationStateStore;
let render: DiRender;
let deleteFileMock: jest.MockedFunction;
- let downloadJson: jest.MockedFunction;
let downloadBinary: jest.MockedFunction;
beforeEach(() => {
@@ -56,9 +53,6 @@ describe("Extensions", () => {
deleteFileMock = jest.fn();
di.override(removePathInjectable, () => deleteFileMock);
- downloadJson = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
- di.override(downloadJsonInjectable, () => downloadJson);
-
downloadBinary = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
di.override(downloadBinaryInjectable, () => downloadBinary);
diff --git a/src/renderer/components/+extensions/attempt-install-by-info.injectable.tsx b/src/renderer/components/+extensions/attempt-install-by-info.injectable.tsx
index 317162cc9f..1f3d65c802 100644
--- a/src/renderer/components/+extensions/attempt-install-by-info.injectable.tsx
+++ b/src/renderer/components/+extensions/attempt-install-by-info.injectable.tsx
@@ -15,7 +15,7 @@ import { reduce } from "lodash";
import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable";
import { withTimeout } from "../../../common/fetch/timeout-controller";
import downloadBinaryInjectable from "../../../common/fetch/download-binary.injectable";
-import downloadJsonInjectable from "../../../common/fetch/download-json.injectable";
+import downloadJsonInjectable from "../../../common/fetch/download-json/normal.injectable";
import type { PackageJson } from "type-fest";
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
import loggerInjectable from "../../../common/logger.injectable";
diff --git a/src/renderer/components/+helm-charts/details/readme-of-selected-helm-chart.injectable.ts b/src/renderer/components/+helm-charts/details/readme-of-selected-helm-chart.injectable.ts
index 839493bed3..8aa61a74da 100644
--- a/src/renderer/components/+helm-charts/details/readme-of-selected-helm-chart.injectable.ts
+++ b/src/renderer/components/+helm-charts/details/readme-of-selected-helm-chart.injectable.ts
@@ -26,11 +26,13 @@ const readmeOfSelectedHelmChartInjectable = getInjectable({
return "";
}
- return await requestHelmChartReadme(
+ const result = await requestHelmChartReadme(
chartVersion.getRepository(),
chartVersion.getName(),
chartVersion.getVersion(),
);
+
+ return result.callWasSuccessful ? result.response : "";
},
valueWhenPending: "",
diff --git a/src/renderer/components/+helm-releases/releases.injectable.ts b/src/renderer/components/+helm-releases/releases.injectable.ts
index ed3d8d6b8e..7f4d2d286b 100644
--- a/src/renderer/components/+helm-releases/releases.injectable.ts
+++ b/src/renderer/components/+helm-releases/releases.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
-import clusterFrameContextInjectable from "../../cluster-frame-context/cluster-frame-context.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import releaseSecretsInjectable from "./release-secrets.injectable";
import requestHelmReleasesInjectable from "../../../common/k8s-api/endpoints/helm-releases.api/request-releases.injectable";
import toHelmReleaseInjectable from "./to-helm-release.injectable";
@@ -13,7 +13,7 @@ const releasesInjectable = getInjectable({
id: "releases",
instantiate: (di) => {
- const clusterContext = di.inject(clusterFrameContextInjectable);
+ const clusterContext = di.inject(clusterFrameContextForNamespacedResourcesInjectable);
const releaseSecrets = di.inject(releaseSecretsInjectable);
const requestHelmReleases = di.inject(requestHelmReleasesInjectable);
const toHelmRelease = di.inject(toHelmReleaseInjectable);
diff --git a/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap b/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap
index 664b0daece..335cc1d159 100644
--- a/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap
+++ b/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[` renders 1`] = `
+exports[` once the subscribe resolves renders 1`] = `
renders 1`] = `
`;
-exports[`
when clicked renders 1`] = `
+exports[`
once the subscribe resolves when clicked renders 1`] = `
when clicked renders 1`] = `
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-2"
tabindex="-1"
- >
-
-
-
- layers
-
-
-
- test-2
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-3
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-4
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-5
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-6
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-7
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-8
-
-
-
- check
-
-
-
-
-
-
-
-
- layers
-
-
-
- test-9
-
-
-
- check
-
-
-
-
-
when clicked renders 1`] = `
when clicked renders 1`] = `
when clicked renders 1`] = `
when clicked renders 1`] = `
+
+
+
+
+ layers
+
+
+
+ test-2
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-3
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-4
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-5
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-6
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-7
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-8
+
+
+
+ check
+
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-9
+
+
+
+ check
+
+
+
+
`;
-exports[` when clicked when 'test-2' is clicked renders 1`] = `
+exports[` once the subscribe resolves when clicked when 'test-2' is clicked renders 1`] = `
when clicked when 'test-2' is clicked renders
`;
-exports[`
when clicked when 'test-2' is clicked when clicked again renders 1`] = `
+exports[`
once the subscribe resolves when clicked when 'test-2' is clicked when clicked again renders 1`] = `
when clicked when 'test-2' is clicked when cl
- test-3
+ test-10
@@ -942,7 +942,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-4
+ test-11
@@ -966,7 +966,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-5
+ test-12
@@ -990,7 +990,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-6
+ test-13
@@ -1014,7 +1014,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-7
+ test-3
@@ -1038,7 +1038,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-8
+ test-4
@@ -1062,7 +1062,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-9
+ test-5
@@ -1086,7 +1086,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-10
+ test-6
@@ -1110,7 +1110,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-11
+ test-7
@@ -1134,7 +1134,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-12
+ test-8
@@ -1158,7 +1158,7 @@ exports[` when clicked when 'test-2' is clicked when cl
- test-13
+ test-9
@@ -1168,7 +1168,7 @@ exports[` when clicked when 'test-2' is clicked when cl
`;
-exports[` when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked renders 1`] = `
+exports[` once the subscribe resolves when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked renders 1`] = `
when clicked when 'test-2' is clicked when cl
`;
-exports[`
when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked when clicked again, then holding down multi select key when 'test-3' is clicked renders 1`] = `
+exports[`
once the subscribe resolves when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked when clicked again, then holding down multi select key when 'test-3' is clicked renders 1`] = `
when clicked when 'test-2' is clicked when cl
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-2"
tabindex="-1"
+ >
+
+
+
+ layers
+
+
+
+ test-10
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-11
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-12
+
+
+
+
+
+
+
+ layers
+
+
+
+ test-13
+
+
+
+
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
-
-
-
-
- layers
-
-
-
- test-10
-
-
-
-
-
-
-
- layers
-
-
-
- test-11
-
-
-
-
-
-
-
- layers
-
-
-
- test-12
-
-
-
-
-
-
-
- layers
-
-
-
- test-13
-
-
-
diff --git a/src/renderer/components/+namespaces/namespace-select-filter.test.tsx b/src/renderer/components/+namespaces/namespace-select-filter.test.tsx
index a81e32e263..0a4626b251 100644
--- a/src/renderer/components/+namespaces/namespace-select-filter.test.tsx
+++ b/src/renderer/components/+namespaces/namespace-select-filter.test.tsx
@@ -3,14 +3,25 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
+import type { AsyncFnMock } from "@async-fn/jest";
+import asyncFn from "@async-fn/jest";
import type { DiContainer } from "@ogre-tools/injectable";
import type { RenderResult } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
import React from "react";
+import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
+import type { Fetch } from "../../../common/fetch/fetch.injectable";
+import fetchInjectable from "../../../common/fetch/fetch.injectable";
import { Namespace } from "../../../common/k8s-api/endpoints";
+import { createMockResponseFromString } from "../../../test-utils/mock-responses";
+import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
+import createClusterInjectable from "../../cluster/create-cluster.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
+import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
+import type { Disposer } from "../../utils";
+import { disposer } from "../../utils";
import { renderFor } from "../test-utils/renderFor";
import { NamespaceSelectFilter } from "./namespace-select-filter";
import type { NamespaceStore } from "./store";
@@ -32,147 +43,197 @@ function createNamespace(name: string): Namespace {
describe("
", () => {
let di: DiContainer;
let namespaceStore: NamespaceStore;
+ let fetchMock: AsyncFnMock
;
let result: RenderResult;
+ let cleanup: Disposer;
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
- di.override(directoryForUserDataInjectable, () => "/some-directory");
+ di.unoverride(subscribeStoresInjectable);
+
+ 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",
+ }));
+
namespaceStore = di.inject(namespaceStoreInjectable);
- const render = renderFor(di);
+ const subscribeStores = di.inject(subscribeStoresInjectable);
- namespaceStore.items.replace([
- createNamespace("test-1"),
- createNamespace("test-2"),
- createNamespace("test-3"),
- createNamespace("test-4"),
- createNamespace("test-5"),
- createNamespace("test-6"),
- createNamespace("test-7"),
- createNamespace("test-8"),
- createNamespace("test-9"),
- createNamespace("test-10"),
- createNamespace("test-11"),
- createNamespace("test-12"),
- createNamespace("test-13"),
- ]);
+ cleanup = disposer(subscribeStores([namespaceStore]));
+
+ fetchMock = asyncFn();
+ di.override(fetchInjectable, () => fetchMock);
+
+ const render = renderFor(di);
result = render((
));
});
- it("renders", () => {
- expect(result.baseElement).toMatchSnapshot();
+ afterEach(() => {
+ cleanup();
});
- describe("when clicked", () => {
- beforeEach(() => {
- result.getByTestId("namespace-select-filter").click();
+ describe("once the subscribe resolves", () => {
+ beforeEach(async () => {
+ await fetchMock.resolveSpecific([
+ "http://127.0.0.1:12345/api-kube/api/v1/namespaces",
+ ], createMockResponseFromString("http://127.0.0.1:12345/api-kube/api/v1/namespaces", JSON.stringify({
+ apiVersion: "v1",
+ kind: "NamespaceList",
+ metadata: {},
+ items: [
+ createNamespace("test-1"),
+ createNamespace("test-2"),
+ createNamespace("test-3"),
+ createNamespace("test-4"),
+ createNamespace("test-5"),
+ createNamespace("test-6"),
+ createNamespace("test-7"),
+ createNamespace("test-8"),
+ createNamespace("test-9"),
+ createNamespace("test-10"),
+ createNamespace("test-11"),
+ createNamespace("test-12"),
+ createNamespace("test-13"),
+ ],
+ })));
});
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
- it("opens menu", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull();
- });
-
- describe("when 'test-2' is clicked", () => {
+ describe("when clicked", () => {
beforeEach(() => {
- result.getByText("test-2").click();
+ result.getByTestId("namespace-select-filter").click();
});
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
- it("has only 'test-2' is selected in the store", () => {
- expect(namespaceStore.contextNamespaces).toEqual(["test-2"]);
+ it("opens menu", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull();
});
- it("closes menu", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
- });
-
- describe("when clicked again", () => {
+ describe("when 'test-2' is clicked", () => {
beforeEach(() => {
- result.getByTestId("namespace-select-filter").click();
+ result.getByText("test-2").click();
});
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
- it("shows 'test-2' as selected", () => {
- expect(result.queryByTestId("namespace-select-filter-option-test-2-selected")).not.toBeNull();
+ it("has only 'test-2' is selected in the store", () => {
+ expect(namespaceStore.contextNamespaces).toEqual(["test-2"]);
});
- it("does not show 'test-1' as selected", () => {
- expect(result.queryByTestId("namespace-select-filter-option-test-1-selected")).toBeNull();
+ it("closes menu", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
});
- describe("when 'test-1' is clicked", () => {
+ describe("when clicked again", () => {
beforeEach(() => {
- result.getByText("test-1").click();
+ result.getByTestId("namespace-select-filter").click();
});
it("renders", () => {
expect(result.baseElement).toMatchSnapshot();
});
- it("has only 'test-1' is selected in the store", () => {
- expect(namespaceStore.contextNamespaces).toEqual(["test-1"]);
+ it("shows 'test-2' as selected", () => {
+ expect(result.queryByTestId("namespace-select-filter-option-test-2-selected")).not.toBeNull();
});
- it("closes menu", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
+ it("does not show 'test-1' as selected", () => {
+ expect(result.queryByTestId("namespace-select-filter-option-test-1-selected")).toBeNull();
});
- describe("when clicked again, then holding down multi select key", () => {
+ describe("when 'test-1' is clicked", () => {
beforeEach(() => {
- const filter = result.getByTestId("namespace-select-filter");
-
- filter.click();
- fireEvent.keyDown(filter, { key: "Meta" });
+ result.getByText("test-1").click();
});
- describe("when 'test-3' is clicked", () => {
+ it("renders", () => {
+ expect(result.baseElement).toMatchSnapshot();
+ });
+
+ it("has only 'test-1' is selected in the store", () => {
+ expect(namespaceStore.contextNamespaces).toEqual(["test-1"]);
+ });
+
+ it("closes menu", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
+ });
+
+ describe("when clicked again, then holding down multi select key", () => {
beforeEach(() => {
- result.getByText("test-3").click();
+ const filter = result.getByTestId("namespace-select-filter");
+
+ filter.click();
+ fireEvent.keyDown(filter, { key: "Meta" });
});
- it("renders", () => {
- expect(result.baseElement).toMatchSnapshot();
- });
-
- it("has both 'test-1' and 'test-3' as selected in the store", () => {
- expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3"]));
- });
-
- it("keeps menu open", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull();
- });
-
- it("does not show 'kube-system' as selected", () => {
- expect(result.queryByTestId("namespace-select-filter-option-kube-system-selected")).toBeNull();
- });
-
- describe("when 'test-13' is clicked", () => {
+ describe("when 'test-3' is clicked", () => {
beforeEach(() => {
- result.getByText("test-13").click();
+ result.getByText("test-3").click();
});
- it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => {
- expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"]));
+ it("renders", () => {
+ expect(result.baseElement).toMatchSnapshot();
});
- it("'test-13' is not sorted to the top of the list", () => {
- const topLevelElement = result.getByText("test-13").parentElement?.parentElement as HTMLElement;
+ it("has both 'test-1' and 'test-3' as selected in the store", () => {
+ expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3"]));
+ });
- expect(topLevelElement.nextSibling).toBe(null);
+ it("keeps menu open", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull();
+ });
+
+ it("does not show 'kube-system' as selected", () => {
+ expect(result.queryByTestId("namespace-select-filter-option-kube-system-selected")).toBeNull();
+ });
+
+ describe("when 'test-13' is clicked", () => {
+ beforeEach(() => {
+ result.getByText("test-13").click();
+ });
+
+ it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => {
+ expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"]));
+ });
+
+ it("'test-13' is not sorted to the top of the list", () => {
+ const topLevelElement = result.getByText("test-13").parentElement?.parentElement as HTMLElement;
+
+ expect(topLevelElement.previousSibling).not.toBe(null);
+ });
+ });
+
+ describe("when releasing multi select key", () => {
+ beforeEach(() => {
+ const filter = result.getByTestId("namespace-select-filter");
+
+ fireEvent.keyUp(filter, { key: "Meta" });
+ });
+
+ it("closes menu", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
+ });
});
});
@@ -183,46 +244,24 @@ describe(" ", () => {
fireEvent.keyUp(filter, { key: "Meta" });
});
- it("closes menu", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
+ it("keeps menu open", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull();
});
});
});
-
- describe("when releasing multi select key", () => {
- beforeEach(() => {
- const filter = result.getByTestId("namespace-select-filter");
-
- fireEvent.keyUp(filter, { key: "Meta" });
- });
-
- it("keeps menu open", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull();
- });
- });
});
});
});
- });
- describe("when multi-selection key is pressed", () => {
- beforeEach(() => {
- const filter = result.getByTestId("namespace-select-filter");
-
- fireEvent.keyDown(filter, { key: "Meta" });
- });
-
- it("should show placeholder text as 'All namespaces'", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).toHaveTextContent("All namespaces");
- });
-
- describe("when 'test-2' is clicked", () => {
+ describe("when multi-selection key is pressed", () => {
beforeEach(() => {
- result.getByText("test-2").click();
+ const filter = result.getByTestId("namespace-select-filter");
+
+ fireEvent.keyDown(filter, { key: "Meta" });
});
- it("should not show placeholder text as 'All namespaces'", () => {
- expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces");
+ it("should show placeholder text as 'All namespaces'", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).toHaveTextContent("All namespaces");
});
describe("when 'test-2' is clicked", () => {
@@ -230,20 +269,30 @@ describe(" ", () => {
result.getByText("test-2").click();
});
- it("should not show placeholder as 'All namespaces'", () => {
+ it("should not show placeholder text as 'All namespaces'", () => {
expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces");
});
- describe("when multi-selection key is raised", () => {
+ describe("when 'test-2' is clicked", () => {
beforeEach(() => {
- const filter = result.getByTestId("namespace-select-filter");
-
- fireEvent.keyUp(filter, { key: "Meta" });
+ result.getByText("test-2").click();
});
- it("should show placeholder text as 'All namespaces'", () => {
+ it("should not show placeholder as 'All namespaces'", () => {
expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces");
});
+
+ describe("when multi-selection key is raised", () => {
+ beforeEach(() => {
+ const filter = result.getByTestId("namespace-select-filter");
+
+ fireEvent.keyUp(filter, { key: "Meta" });
+ });
+
+ it("should show placeholder text as 'All namespaces'", () => {
+ expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces");
+ });
+ });
});
});
});
diff --git a/src/renderer/components/+namespaces/store.injectable.ts b/src/renderer/components/+namespaces/store.injectable.ts
index 68d611e837..eb2a7e4a35 100644
--- a/src/renderer/components/+namespaces/store.injectable.ts
+++ b/src/renderer/components/+namespaces/store.injectable.ts
@@ -9,6 +9,8 @@ import createStorageInjectable from "../../utils/create-storage/create-storage.i
import namespaceApiInjectable from "../../../common/k8s-api/endpoints/namespace.api.injectable";
import assert from "assert";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
+import clusterConfiguredAccessibleNamespacesInjectable from "../../cluster/accessible-namespaces.injectable";
const namespaceStoreInjectable = getInjectable({
id: "namespace-store",
@@ -20,7 +22,9 @@ const namespaceStoreInjectable = getInjectable({
const api = di.inject(namespaceApiInjectable);
return new NamespaceStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
storage: createStorage("selected_namespaces", undefined),
+ clusterConfiguredAccessibleNamespaces: di.inject(clusterConfiguredAccessibleNamespacesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+namespaces/store.ts b/src/renderer/components/+namespaces/store.ts
index 1b2342facf..4c178a8a96 100644
--- a/src/renderer/components/+namespaces/store.ts
+++ b/src/renderer/components/+namespaces/store.ts
@@ -3,22 +3,23 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { IReactionDisposer } from "mobx";
+import type { IComputedValue, IReactionDisposer } from "mobx";
import { action, comparer, computed, makeObservable, reaction } from "mobx";
import type { StorageLayer } from "../../utils";
import { autoBind, noop, toggle } from "../../utils";
-import type { KubeObjectStoreLoadingParams } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreLoadingParams } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { NamespaceApi } from "../../../common/k8s-api/endpoints/namespace.api";
import { Namespace } from "../../../common/k8s-api/endpoints/namespace.api";
-interface Dependencies {
- storage: StorageLayer;
+interface Dependencies extends KubeObjectStoreDependencies {
+ readonly storage: StorageLayer;
+ readonly clusterConfiguredAccessibleNamespaces: IComputedValue;
}
export class NamespaceStore extends KubeObjectStore {
constructor(protected readonly dependencies: Dependencies, api: NamespaceApi) {
- super(api);
+ super(dependencies, api);
makeObservable(this);
autoBind(this);
@@ -26,11 +27,21 @@ export class NamespaceStore extends KubeObjectStore {
}
private async init() {
- await this.contextReady;
await this.dependencies.storage.whenReady;
- this.selectNamespaces(this.initialNamespaces);
- this.autoLoadAllowedNamespaces();
+ const { allowedNamespaces } = this;
+ const selectedNamespaces = this.dependencies.storage.get(); // raw namespaces, undefined on first load
+
+ // return previously saved namespaces from local-storage (if any)
+ if (Array.isArray(selectedNamespaces)) {
+ this.selectNamespaces(selectedNamespaces.filter(namespace => allowedNamespaces.includes(namespace)));
+ } else if (allowedNamespaces.includes("default")) {
+ this.selectNamespaces(["default"]);
+ } else if (allowedNamespaces.length) {
+ this.selectNamespaces([allowedNamespaces[0]]);
+ } else {
+ this.selectNamespaces([]);
+ }
}
public onContextChange(callback: (namespaces: string[]) => void, opts: { fireImmediately?: boolean } = {}): IReactionDisposer {
@@ -40,32 +51,6 @@ export class NamespaceStore extends KubeObjectStore {
});
}
- private autoLoadAllowedNamespaces(): IReactionDisposer {
- return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), {
- fireImmediately: true,
- equals: comparer.shallow,
- });
- }
-
- private get initialNamespaces(): string[] {
- const { allowedNamespaces } = this;
- const selectedNamespaces = this.dependencies.storage.get(); // raw namespaces, undefined on first load
-
- // return previously saved namespaces from local-storage (if any)
- if (Array.isArray(selectedNamespaces)) {
- return selectedNamespaces.filter(namespace => allowedNamespaces.includes(namespace));
- }
-
- // otherwise select "default" or first allowed namespace
- if (allowedNamespaces.includes("default")) {
- return ["default"];
- } else if (allowedNamespaces.length) {
- return [allowedNamespaces[0]];
- }
-
- return [];
- }
-
/**
* @private
* The current value (list of namespaces names) in the storage layer
@@ -75,10 +60,7 @@ export class NamespaceStore extends KubeObjectStore {
}
@computed get allowedNamespaces(): string[] {
- return Array.from(new Set([
- ...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
- ...this.items.map(item => item.getName()), // loaded namespaces from k8s api
- ].flat()));
+ return this.items.map(item => item.getName());
}
/**
@@ -110,11 +92,13 @@ export class NamespaceStore extends KubeObjectStore {
}
subscribe() {
+ const clusterConfiguredAccessibleNamespaces = this.dependencies.clusterConfiguredAccessibleNamespaces.get();
+
/**
* if user has given static list of namespaces let's not start watches
* because watch adds stuff that's not wanted or will just fail
*/
- if ((this.context?.cluster.accessibleNamespaces.length ?? 0) > 0) {
+ if (clusterConfiguredAccessibleNamespaces.length > 0) {
return noop;
}
@@ -122,17 +106,13 @@ export class NamespaceStore extends KubeObjectStore {
}
protected async loadItems(params: KubeObjectStoreLoadingParams): Promise {
- const { allowedNamespaces } = this;
+ const clusterConfiguredAccessibleNamespaces = this.dependencies.clusterConfiguredAccessibleNamespaces.get();
- let namespaces = await super.loadItems(params).catch(() => []);
-
- namespaces = namespaces.filter(namespace => allowedNamespaces.includes(namespace.getName()));
-
- if (!namespaces.length && allowedNamespaces.length > 0) {
- return allowedNamespaces.map(getDummyNamespace);
+ if (clusterConfiguredAccessibleNamespaces.length > 0) {
+ return clusterConfiguredAccessibleNamespaces.map(getDummyNamespace);
}
- return namespaces;
+ return super.loadItems(params);
}
@action selectNamespaces = (namespace: string | string[]) => {
diff --git a/src/renderer/components/+network-endpoints/store.injectable.ts b/src/renderer/components/+network-endpoints/store.injectable.ts
index a63014ea70..7531b933b3 100644
--- a/src/renderer/components/+network-endpoints/store.injectable.ts
+++ b/src/renderer/components/+network-endpoints/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import endpointsApiInjectable from "../../../common/k8s-api/endpoints/endpoint.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { EndpointsStore } from "./store";
@@ -16,7 +17,9 @@ const endpointsStoreInjectable = getInjectable({
const api = di.inject(endpointsApiInjectable);
- return new EndpointsStore(api);
+ return new EndpointsStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+network-ingresses/store.injectable.ts b/src/renderer/components/+network-ingresses/store.injectable.ts
index 9b95939e9f..0084a006a2 100644
--- a/src/renderer/components/+network-ingresses/store.injectable.ts
+++ b/src/renderer/components/+network-ingresses/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import ingressApiInjectable from "../../../common/k8s-api/endpoints/ingress.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { IngressStore } from "./store";
@@ -16,7 +17,9 @@ const ingressStoreInjectable = getInjectable({
const api = di.inject(ingressApiInjectable);
- return new IngressStore(api);
+ return new IngressStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+network-policies/store.injectable.ts b/src/renderer/components/+network-policies/store.injectable.ts
index a666c04cb4..ad79a89b54 100644
--- a/src/renderer/components/+network-policies/store.injectable.ts
+++ b/src/renderer/components/+network-policies/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import networkPolicyApiInjectable from "../../../common/k8s-api/endpoints/network-policy.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { NetworkPolicyStore } from "./store";
@@ -16,7 +17,9 @@ const networkPolicyStoreInjectable = getInjectable({
const api = di.inject(networkPolicyApiInjectable);
- return new NetworkPolicyStore(api);
+ return new NetworkPolicyStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+network-services/store.injectable.ts b/src/renderer/components/+network-services/store.injectable.ts
index 6013a97f15..e54f994162 100644
--- a/src/renderer/components/+network-services/store.injectable.ts
+++ b/src/renderer/components/+network-services/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { ServiceStore } from "./store";
@@ -16,7 +17,9 @@ const serviceStoreInjectable = getInjectable({
const api = di.inject(serviceApiInjectable);
- return new ServiceStore(api);
+ return new ServiceStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+nodes/details.tsx b/src/renderer/components/+nodes/details.tsx
index 4e1eeddb65..622f9f1b69 100644
--- a/src/renderer/components/+nodes/details.tsx
+++ b/src/renderer/components/+nodes/details.tsx
@@ -23,6 +23,7 @@ import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.inj
import type { PodStore } from "../+workloads-pods/store";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import loggerInjectable from "../../../common/logger.injectable";
+import loadPodsFromAllNamespacesInjectable from "../+workloads-pods/load-pods-from-all-namespaces.injectable";
export interface NodeDetailsProps extends KubeObjectDetailsProps {
}
@@ -31,6 +32,7 @@ interface Dependencies {
subscribeStores: SubscribeStores;
podStore: PodStore;
logger: Logger;
+ loadPodsFromAllNamespaces: () => void;
}
@observer
@@ -41,6 +43,8 @@ class NonInjectedNodeDetails extends React.Component(NonIn
subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable),
logger: di.inject(loggerInjectable),
+ loadPodsFromAllNamespaces: di.inject(loadPodsFromAllNamespacesInjectable),
}),
});
diff --git a/src/renderer/components/+nodes/store.injectable.ts b/src/renderer/components/+nodes/store.injectable.ts
index adb69a99ad..ac149210bd 100644
--- a/src/renderer/components/+nodes/store.injectable.ts
+++ b/src/renderer/components/+nodes/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { NodeStore } from "./store";
@@ -16,7 +17,9 @@ const nodeStoreInjectable = getInjectable({
const api = di.inject(nodeApiInjectable);
- return new NodeStore(api);
+ return new NodeStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+nodes/store.ts b/src/renderer/components/+nodes/store.ts
index 9f4be4b620..87fafa9256 100644
--- a/src/renderer/components/+nodes/store.ts
+++ b/src/renderer/components/+nodes/store.ts
@@ -6,13 +6,13 @@ import { sum } from "lodash";
import { computed, makeObservable } from "mobx";
import type { Node, NodeApi } from "../../../common/k8s-api/endpoints";
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import { autoBind } from "../../utils";
export class NodeStore extends KubeObjectStore {
- constructor(api: NodeApi, opts?: KubeObjectStoreOptions) {
- super(api, opts);
+ constructor(dependencies: KubeObjectStoreDependencies, api: NodeApi, opts?: KubeObjectStoreOptions) {
+ super(dependencies, api, opts);
makeObservable(this);
autoBind(this);
diff --git a/src/renderer/components/+pod-security-policies/store.injectable.ts b/src/renderer/components/+pod-security-policies/store.injectable.ts
index b010b60c97..44ab372d70 100644
--- a/src/renderer/components/+pod-security-policies/store.injectable.ts
+++ b/src/renderer/components/+pod-security-policies/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import podSecurityPolicyApiInjectable from "../../../common/k8s-api/endpoints/pod-security-policy.api.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PodSecurityPolicyStore } from "./store";
@@ -16,7 +17,9 @@ const podSecurityPolicyStoreInjectable = getInjectable({
const api = di.inject(podSecurityPolicyApiInjectable);
- return new PodSecurityPolicyStore(api);
+ return new PodSecurityPolicyStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+storage-classes/store.injectable.ts b/src/renderer/components/+storage-classes/store.injectable.ts
index ab4ae8c784..17f6da93db 100644
--- a/src/renderer/components/+storage-classes/store.injectable.ts
+++ b/src/renderer/components/+storage-classes/store.injectable.ts
@@ -7,6 +7,7 @@ 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 storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { StorageClassStore } from "./store";
@@ -19,6 +20,7 @@ const storageClassStoreInjectable = getInjectable({
return new StorageClassStore({
getPersistentVolumesByStorageClass: di.inject(getPersistentVolumesByStorageClassInjectable),
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+storage-classes/store.ts b/src/renderer/components/+storage-classes/store.ts
index 8037a8d67d..8c01c145b3 100644
--- a/src/renderer/components/+storage-classes/store.ts
+++ b/src/renderer/components/+storage-classes/store.ts
@@ -3,12 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { StorageClass, StorageClassApi, StorageClassData } from "../../../common/k8s-api/endpoints/storage-class.api";
import type { GetPersistentVolumesByStorageClass } from "../+storage-volumes/get-persisten-volumes-by-storage-class.injectable";
-export interface StorageClassStoreDependencies {
+export interface StorageClassStoreDependencies extends KubeObjectStoreDependencies {
getPersistentVolumesByStorageClass: GetPersistentVolumesByStorageClass;
}
@@ -18,7 +18,7 @@ export class StorageClassStore extends KubeObjectStore {
let render: DiRender;
@@ -20,8 +23,19 @@ describe("ClusterRoleBindingDialog tests", () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
+ di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
+ di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
- di.override(directoryForUserDataInjectable, () => "/some-path-for-user-data");
+
+ 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);
diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts b/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts
index afaf3e7400..a2e3223fa7 100644
--- a/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts
+++ b/src/renderer/components/+user-management/+cluster-role-bindings/store.injectable.ts
@@ -8,6 +8,7 @@ import { storesAndApisCanBeCreatedInjectionToken } from "../../../../common/k8s-
import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role-binding.api.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ClusterRoleBindingStore } from "./store";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable";
const clusterRoleBindingStoreInjectable = getInjectable({
id: "cluster-role-binding-store",
@@ -16,7 +17,9 @@ const clusterRoleBindingStoreInjectable = getInjectable({
const api = di.inject(clusterRoleBindingApiInjectable);
- return new ClusterRoleBindingStore(api);
+ return new ClusterRoleBindingStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts
index bfd83f3b3b..9ae877f635 100644
--- a/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts
+++ b/src/renderer/components/+user-management/+cluster-roles/store.injectable.ts
@@ -8,6 +8,7 @@ import { storesAndApisCanBeCreatedInjectionToken } from "../../../../common/k8s-
import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role.api.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ClusterRoleStore } from "./store";
+import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable";
const clusterRoleStoreInjectable = getInjectable({
id: "cluster-role-store",
@@ -16,7 +17,9 @@ const clusterRoleStoreInjectable = getInjectable({
const api = di.inject(clusterRoleApiInjectable);
- return new ClusterRoleStore(api);
+ return new ClusterRoleStore({
+ context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx
index 82e3a7c9d7..08067ed817 100644
--- a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx
+++ b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx
@@ -13,6 +13,9 @@ import { renderFor } from "../../../test-utils/renderFor";
import directoryForUserDataInjectable from "../../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable";
import storesAndApisCanBeCreatedInjectable from "../../../../stores-apis-can-be-created.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 "../../../../cluster/create-cluster.injectable";
describe("RoleBindingDialog tests", () => {
let render: DiRender;
@@ -20,8 +23,19 @@ describe("RoleBindingDialog tests", () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
+ di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
+ di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
- di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
+
+ 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);
diff --git a/src/renderer/components/+user-management/+role-bindings/store.injectable.ts b/src/renderer/components/+user-management/+role-bindings/store.injectable.ts
index 20122b47d6..a3f33e76bc 100644
--- a/src/renderer/components/+user-management/+role-bindings/store.injectable.ts
+++ b/src/renderer/components/+user-management/+role-bindings/store.injectable.ts
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import roleBindingApiInjectable from "../../../../common/k8s-api/endpoints/role-binding.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import { RoleBindingStore } from "./store";
@@ -16,7 +17,9 @@ const roleBindingStoreInjectable = getInjectable({
const api = di.inject(roleBindingApiInjectable);
- return new RoleBindingStore(api);
+ return new RoleBindingStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+user-management/+roles/store.injectable.ts b/src/renderer/components/+user-management/+roles/store.injectable.ts
index ac10dc831b..8b86ddb0ab 100644
--- a/src/renderer/components/+user-management/+roles/store.injectable.ts
+++ b/src/renderer/components/+user-management/+roles/store.injectable.ts
@@ -8,6 +8,7 @@ import roleApiInjectable from "../../../../common/k8s-api/endpoints/role.api.inj
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { RoleStore } from "./store";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
const roleStoreInjectable = getInjectable({
id: "role-store",
@@ -16,7 +17,9 @@ const roleStoreInjectable = getInjectable({
const api = di.inject(roleApiInjectable);
- return new RoleStore(api);
+ return new RoleStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts
index 75b4b791d5..98570c9017 100644
--- a/src/renderer/components/+user-management/+service-accounts/store.injectable.ts
+++ b/src/renderer/components/+user-management/+service-accounts/store.injectable.ts
@@ -8,6 +8,7 @@ import serviceAccountApiInjectable from "../../../../common/k8s-api/endpoints/se
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ServiceAccountStore } from "./store";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
const serviceAccountStoreInjectable = getInjectable({
id: "service-account-store",
@@ -16,7 +17,9 @@ const serviceAccountStoreInjectable = getInjectable({
const api = di.inject(serviceAccountApiInjectable);
- return new ServiceAccountStore(api);
+ return new ServiceAccountStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ }, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
diff --git a/src/renderer/components/+workloads-cronjobs/store.injectable.ts b/src/renderer/components/+workloads-cronjobs/store.injectable.ts
index a71352685f..6b0fab2c9c 100644
--- a/src/renderer/components/+workloads-cronjobs/store.injectable.ts
+++ b/src/renderer/components/+workloads-cronjobs/store.injectable.ts
@@ -7,6 +7,7 @@ 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 cronJobApiInjectable from "../../../common/k8s-api/endpoints/cron-job.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { CronJobStore } from "./store";
@@ -19,6 +20,7 @@ const cronJobStoreInjectable = getInjectable({
return new CronJobStore({
getJobsByOwner: di.inject(getJobsByOwnerInjectable),
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+workloads-cronjobs/store.ts b/src/renderer/components/+workloads-cronjobs/store.ts
index 67e006ac90..fe6c765723 100644
--- a/src/renderer/components/+workloads-cronjobs/store.ts
+++ b/src/renderer/components/+workloads-cronjobs/store.ts
@@ -3,18 +3,18 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { CronJob, CronJobApi } from "../../../common/k8s-api/endpoints/cron-job.api";
import type { GetJobsByOwner } from "../+workloads-jobs/get-jobs-by-owner.injectable";
-interface Dependencies {
+interface Dependencies extends KubeObjectStoreDependencies {
getJobsByOwner: GetJobsByOwner;
}
export class CronJobStore extends KubeObjectStore {
constructor(protected readonly dependencies: Dependencies, api: CronJobApi, opts?: KubeObjectStoreOptions) {
- super(api, opts);
+ super(dependencies, api, opts);
}
getStatuses(cronJobs?: CronJob[]) {
diff --git a/src/renderer/components/+workloads-daemonsets/store.injectable.ts b/src/renderer/components/+workloads-daemonsets/store.injectable.ts
index 3b7611dc43..5711ad625f 100644
--- a/src/renderer/components/+workloads-daemonsets/store.injectable.ts
+++ b/src/renderer/components/+workloads-daemonsets/store.injectable.ts
@@ -9,6 +9,7 @@ import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import { DaemonSetStore } from "./store";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
const daemonSetStoreInjectable = getInjectable({
id: "daemon-set-store",
@@ -19,6 +20,7 @@ const daemonSetStoreInjectable = getInjectable({
return new DaemonSetStore({
getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable),
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+workloads-daemonsets/store.ts b/src/renderer/components/+workloads-daemonsets/store.ts
index 66216e3f76..1345468b62 100644
--- a/src/renderer/components/+workloads-daemonsets/store.ts
+++ b/src/renderer/components/+workloads-daemonsets/store.ts
@@ -6,16 +6,16 @@
import type { GetPodsByOwnerId } from "../+workloads-pods/get-pods-by-owner-id.injectable";
import type { DaemonSet, DaemonSetApi, Pod } from "../../../common/k8s-api/endpoints";
import { PodStatusPhase } from "../../../common/k8s-api/endpoints";
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
-export interface DaemonSetStoreDependencies {
+export interface DaemonSetStoreDependencies extends KubeObjectStoreDependencies {
readonly getPodsByOwnerId: GetPodsByOwnerId;
}
export class DaemonSetStore extends KubeObjectStore {
constructor(protected readonly dependencies: DaemonSetStoreDependencies, api: DaemonSetApi, opts?: KubeObjectStoreOptions) {
- super(api, opts);
+ super(dependencies, api, opts);
}
getChildPods(daemonSet: DaemonSet): Pod[] {
diff --git a/src/renderer/components/+workloads-deployments/store.injectable.ts b/src/renderer/components/+workloads-deployments/store.injectable.ts
index 02eb3da48c..b77d9aa496 100644
--- a/src/renderer/components/+workloads-deployments/store.injectable.ts
+++ b/src/renderer/components/+workloads-deployments/store.injectable.ts
@@ -9,6 +9,7 @@ import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manag
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";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
const deploymentStoreInjectable = getInjectable({
id: "deployment-store",
@@ -19,6 +20,7 @@ const deploymentStoreInjectable = getInjectable({
return new DeploymentStore({
podStore: di.inject(podStoreInjectable),
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+workloads-deployments/store.ts b/src/renderer/components/+workloads-deployments/store.ts
index 014f1c2abc..af5d085594 100644
--- a/src/renderer/components/+workloads-deployments/store.ts
+++ b/src/renderer/components/+workloads-deployments/store.ts
@@ -6,7 +6,7 @@
import type { PodStore } from "../+workloads-pods/store";
import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints";
import { PodStatusPhase } from "../../../common/k8s-api/endpoints";
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
// This needs to be disables because of https://github.com/microsoft/TypeScript/issues/15300
@@ -17,13 +17,13 @@ export type DeploymentStatuses = {
pending: number;
};
-export interface DeploymentStoreDependencies {
+export interface DeploymentStoreDependencies extends KubeObjectStoreDependencies {
readonly podStore: PodStore;
}
export class DeploymentStore extends KubeObjectStore {
constructor(protected readonly dependencies: DeploymentStoreDependencies, api: DeploymentApi, opts?: KubeObjectStoreOptions) {
- super(api, opts);
+ super(dependencies, api, opts);
}
protected sortItems(items: Deployment[]) {
diff --git a/src/renderer/components/+workloads-jobs/store.injectable.ts b/src/renderer/components/+workloads-jobs/store.injectable.ts
index 46e4d26fb4..8f394a1bf5 100644
--- a/src/renderer/components/+workloads-jobs/store.injectable.ts
+++ b/src/renderer/components/+workloads-jobs/store.injectable.ts
@@ -7,6 +7,7 @@ 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 jobApiInjectable from "../../../common/k8s-api/endpoints/job.api.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { JobStore } from "./store";
@@ -19,6 +20,7 @@ const jobStoreInjectable = getInjectable({
return new JobStore({
getPodsByOwnerId: di.inject(getPodsByOwnerIdInjectable),
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
diff --git a/src/renderer/components/+workloads-jobs/store.ts b/src/renderer/components/+workloads-jobs/store.ts
index 47fc09d931..2f6f671aad 100644
--- a/src/renderer/components/+workloads-jobs/store.ts
+++ b/src/renderer/components/+workloads-jobs/store.ts
@@ -3,20 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
+import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { Job, JobApi } from "../../../common/k8s-api/endpoints/job.api";
import type { CronJob, Pod } from "../../../common/k8s-api/endpoints";
import { PodStatusPhase } from "../../../common/k8s-api/endpoints";
import type { GetPodsByOwnerId } from "../+workloads-pods/get-pods-by-owner-id.injectable";
-interface Dependencies {
+interface Dependencies extends KubeObjectStoreDependencies {
getPodsByOwnerId: GetPodsByOwnerId;
}
export class JobStore extends KubeObjectStore {
constructor(protected readonly dependencies: Dependencies, api: JobApi, opts?: KubeObjectStoreOptions) {
- super(api, opts);
+ super(dependencies, api, opts);
}
getChildPods(job: Job): Pod[] {
diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx
index 2430409d30..19c81712a9 100644
--- a/src/renderer/components/+workloads-overview/overview-statuses.tsx
+++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx
@@ -12,6 +12,7 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import type { IComputedValue } from "mobx";
import workloadsInjectable from "./workloads/workloads.injectable";
import type { Workload } from "./workloads/workload-injection-token";
+import { formatKubeApiResource } from "../../../common/rbac";
export interface OverviewStatusesProps {}
@@ -24,7 +25,7 @@ const NonInjectedOverviewStatuses = observer(