From e3cb23e1118061512066240c930cf9d8ac42b0b1 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 30 May 2022 11:15:51 -0700 Subject: [PATCH] release v5.5.2 (#5504) Co-authored-by: Lauri Nevala Co-authored-by: Roman --- .../resources/03-statefulset.yml.hb | 5 - .../resources/10-node-exporter-ds.yml.hb | 5 - .../14-kube-state-metrics-deployment.yml.hb | 5 - package.json | 5 +- src/common/k8s-api/endpoints/pods.api.ts | 98 +++++--- src/common/k8s-api/kube-json-api.ts | 1 + src/jest-after-env.setup.ts | 5 + .../__snapshots__/projected.test.tsx.snap | 229 ++++++++++++++++++ .../__snapshots__/ceph-fs.test.tsx.snap | 229 ++++++++++++++++++ .../variants/__tests__/ceph-fs.test.tsx | 116 +++++++++ .../details/volumes/variants/ceph-fs.tsx | 4 +- .../volumes/variants/projected.test.tsx | 197 +++++++++++++++ .../details/volumes/variants/projected.tsx | 18 +- .../monaco-editor/monaco-editor.tsx | 15 +- src/renderer/utils/display-mode.ts | 11 + src/renderer/utils/index.ts | 1 + 16 files changed, 875 insertions(+), 69 deletions(-) create mode 100644 src/jest-after-env.setup.ts create mode 100644 src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap create mode 100644 src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap create mode 100644 src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx create mode 100644 src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx create mode 100644 src/renderer/utils/display-mode.ts diff --git a/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb b/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb index cc177204a3..288cd553b1 100644 --- a/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb +++ b/extensions/metrics-cluster-feature/resources/03-statefulset.yml.hb @@ -24,11 +24,6 @@ spec: operator: In values: - linux - - matchExpressions: - - key: beta.kubernetes.io/os - operator: In - values: - - linux # <%- if config.node_selector -%> # nodeSelector: # <%- node_selector.to_h.each do |key, value| -%> 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 index 2ff46d8d0b..c02fb93321 100644 --- a/extensions/metrics-cluster-feature/resources/10-node-exporter-ds.yml.hb +++ b/extensions/metrics-cluster-feature/resources/10-node-exporter-ds.yml.hb @@ -30,11 +30,6 @@ spec: operator: In values: - linux - - matchExpressions: - - key: beta.kubernetes.io/os - operator: In - values: - - linux securityContext: runAsNonRoot: true runAsUser: 65534 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 index 5eaefe2cf9..0174d5c8f4 100644 --- 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 @@ -23,11 +23,6 @@ spec: operator: In values: - linux - - matchExpressions: - - key: beta.kubernetes.io/os - operator: In - values: - - linux serviceAccountName: kube-state-metrics containers: - name: kube-state-metrics diff --git a/package.json b/package.json index ef115cda6c..d28b2b1a52 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.5.1", + "version": "5.5.2", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", @@ -72,6 +72,9 @@ "/src/jest.setup.ts", "jest-canvas-mock" ], + "setupFilesAfterEnv": [ + "/src/jest-after-env.setup.ts" + ], "globals": { "ts-jest": { "isolatedModules": true diff --git a/src/common/k8s-api/endpoints/pods.api.ts b/src/common/k8s-api/endpoints/pods.api.ts index c76a3d021c..878b819802 100644 --- a/src/common/k8s-api/endpoints/pods.api.ts +++ b/src/common/k8s-api/endpoints/pods.api.ts @@ -279,8 +279,10 @@ export interface CephfsSource { secretRef?: SecretReference; /** * Whether the filesystem is used as readOnly. + * + * @default false */ - readOnly: boolean; + readOnly?: boolean; } export interface CinderSource { @@ -430,46 +432,62 @@ export interface PortworxVolumeSource { readOnly?: boolean; } +export interface KeyToPath { + key: string; + path: string; + mode?: number; +} + +export interface ConfigMapProjection { + name: string; + items?: KeyToPath[]; + optional?: boolean; +} + +export interface ObjectFieldSelector { + fieldPath: string; + apiVersion?: string; +} + +export interface ResourceFieldSelector { + resource: string; + containerName?: string; + divisor?: string; +} + +export interface DownwardAPIVolumeFile { + path: string; + fieldRef?: ObjectFieldSelector; + resourceFieldRef?: ResourceFieldSelector; + mode?: number; +} + +export interface DownwardAPIProjection { + items?: DownwardAPIVolumeFile[]; +} + +export interface SecretProjection { + name: string; + items?: KeyToPath[]; + optional?: boolean; +} + +export interface ServiceAccountTokenProjection { + audience?: string; + expirationSeconds?: number; + path: string; +} + +export interface VolumeProjection { + secret?: SecretProjection; + downwardAPI?: DownwardAPIProjection; + configMap?: ConfigMapProjection; + serviceAccountToken?: ServiceAccountTokenProjection; +} + export interface ProjectedSource { - sources: { - secret?: { - name: string; - items?: { - key: string; - path: string; - mode?: number; - }[]; - }; - downwardAPI?: { - items?: { - path: string; - fieldRef?: { - fieldPath: string; - apiVersion?: string; - }; - resourceFieldRef?: { - resource: string; - containerName?: string; - }; - mode?: number; - }[]; - }; - configMap?: { - name: string; - items?: { - key: string; - path: string; - mode?: number; - }[]; - optional?: boolean; - }; - serviceAccountToken?: { - audience?: string; - expirationSeconds?: number; - path: string; - }; - }[]; - defaultMode: number; + sources?: VolumeProjection[]; + defaultMode?: number; } export interface QuobyteSource { diff --git a/src/common/k8s-api/kube-json-api.ts b/src/common/k8s-api/kube-json-api.ts index 258c61af5e..9bde5201f4 100644 --- a/src/common/k8s-api/kube-json-api.ts +++ b/src/common/k8s-api/kube-json-api.ts @@ -43,6 +43,7 @@ export interface KubeJsonApiData extends JsonApiData { kind: string; apiVersion: string; metadata: KubeJsonApiMetadata; + spec?: unknown; } export interface KubeJsonApiError extends JsonApiError { diff --git a/src/jest-after-env.setup.ts b/src/jest-after-env.setup.ts new file mode 100644 index 0000000000..b9ee36c4cf --- /dev/null +++ b/src/jest-after-env.setup.ts @@ -0,0 +1,5 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import "@testing-library/jest-dom/extend-expect"; diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap b/src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap new file mode 100644 index 0000000000..ef0d3dd261 --- /dev/null +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/__snapshots__/projected.test.tsx.snap @@ -0,0 +1,229 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders 1`] = ` + +
+
+ + Sources + + +
+
+ +`; + +exports[` renders a secret source including overriding mode 1`] = ` + +
+
+ + Default Mount Mode + + + 0o777 + +
+
+ + Sources + + +
+ Secret +
+
+ + Name + + + my-projected-secret + +
+
+ + Items + + +
    +
  • + foo⇢/bar + (0o666) +
  • +
+
+
+
+
+
+ +`; + +exports[` renders a secret source, when provided 1`] = ` + +
+
+ + Default Mount Mode + + + 0o777 + +
+
+ + Sources + + +
+ Secret +
+
+ + Name + + + my-projected-secret + +
+
+ + Items + + +
    +
  • + foo⇢/bar +
  • +
+
+
+
+
+
+ +`; + +exports[` renders default mount mode in octal when provided 1`] = ` + +
+
+ + Default Mount Mode + + + 0o777 + +
+
+ + Sources + + +
+
+ +`; + +exports[` renders when no sources array provided 1`] = ` + +
+
+ + Default Mount Mode + + + 0o777 + +
+
+ + Sources + + +
+
+ +`; diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap new file mode 100644 index 0000000000..627b2fb1a3 --- /dev/null +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/__snapshots__/ceph-fs.test.tsx.snap @@ -0,0 +1,229 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render 'false' for Readonly when false is provided 1`] = ` +
+
+ + Monitors + + +
    + +
+
+ + Mount Path + + + / + +
+
+ + Username + + + admin + +
+
+ + Secret Filepath + + + /etc/ceph/user.secret + +
+
+ + Readonly + + + false + +
+
+`; + +exports[` should render 'false' for Readonly when not provided 1`] = ` +
+
+ + Monitors + + +
    + +
+
+ + Mount Path + + + / + +
+
+ + Username + + + admin + +
+
+ + Secret Filepath + + + /etc/ceph/user.secret + +
+
+ + Readonly + + + false + +
+
+`; + +exports[` should render 'true' for Readonly when true is provided 1`] = ` +
+
+ + Monitors + + +
    + +
+
+ + Mount Path + + + / + +
+
+ + Username + + + admin + +
+
+ + Secret Filepath + + + /etc/ceph/user.secret + +
+
+ + Readonly + + + true + +
+
+`; diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx new file mode 100644 index 0000000000..5555332328 --- /dev/null +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/__tests__/ceph-fs.test.tsx @@ -0,0 +1,116 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { render } from "@testing-library/react"; +import React from "react"; +import type { CephfsSource } from "../../../../../../../common/k8s-api/endpoints"; +import { Pod } from "../../../../../../../common/k8s-api/endpoints"; +import { CephFs } from "../ceph-fs"; + +describe("", () => { + it("should render 'false' for Readonly when not provided", () => { + const cephfsName = "my-ceph"; + const cephfsVolume: CephfsSource = { + monitors: [], + }; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: cephfsName, + cephfs: cephfsVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.container).toMatchSnapshot(); + expect(result.getByTestId("cephfs-readonly")).toHaveTextContent("false"); + }); + + it("should render 'false' for Readonly when false is provided", () => { + const cephfsName = "my-ceph"; + const cephfsVolume: CephfsSource = { + monitors: [], + readOnly: false, + }; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: cephfsName, + cephfs: cephfsVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.container).toMatchSnapshot(); + expect(result.getByTestId("cephfs-readonly")).toHaveTextContent("false"); + }); + + it("should render 'true' for Readonly when true is provided", () => { + const cephfsName = "my-ceph"; + const cephfsVolume: CephfsSource = { + monitors: [], + readOnly: true, + }; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: cephfsName, + cephfs: cephfsVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.container).toMatchSnapshot(); + expect(result.getByTestId("cephfs-readonly")).toHaveTextContent("true"); + }); +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx index d283a93f9d..af1e115721 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/ceph-fs.tsx @@ -10,7 +10,7 @@ import type { VolumeVariantComponent } from "../variant-helpers"; import { LocalRef } from "../variant-helpers"; export const CephFs: VolumeVariantComponent<"cephfs"> = ( - ({ pod, variant: { monitors, path = "/", user = "admin", secretFile = "/etc/ceph/user.secret", secretRef, readOnly }}) => ( + ({ pod, variant: { monitors, path = "/", user = "admin", secretFile = "/etc/ceph/user.secret", secretRef, readOnly = false }}) => ( <>
    @@ -39,7 +39,7 @@ export const CephFs: VolumeVariantComponent<"cephfs"> = ( ) } - + {readOnly.toString()} diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx new file mode 100644 index 0000000000..24405bc1a9 --- /dev/null +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/projected.test.tsx @@ -0,0 +1,197 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { render } from "@testing-library/react"; +import React from "react"; +import type { ProjectedSource } from "../../../../../../common/k8s-api/endpoints"; +import { Pod } from "../../../../../../common/k8s-api/endpoints"; +import { Projected } from "./projected"; + +describe("", () => { + it("renders", () => { + const projectedVolume: ProjectedSource = { }; + const projectedVolumeName = "my-projected"; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: projectedVolumeName, + projected: projectedVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.baseElement).toMatchSnapshot(); + }); + + it("renders default mount mode in octal when provided", () => { + const projectedVolume: ProjectedSource = { + defaultMode: 0o777, + sources: [], + }; + const projectedVolumeName = "my-projected"; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: projectedVolumeName, + projected: projectedVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.baseElement).toMatchSnapshot(); + }); + + it("renders when no sources array provided", () => { + const projectedVolume: ProjectedSource = { + defaultMode: 0o777, + }; + const projectedVolumeName = "my-projected"; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: projectedVolumeName, + projected: projectedVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.baseElement).toMatchSnapshot(); + }); + + it("renders a secret source, when provided", () => { + const projectedVolume: ProjectedSource = { + defaultMode: 0o777, + sources: [{ + secret: { + name: "my-projected-secret", + items: [{ + key: "foo", + path: "/bar", + }], + }, + }], + }; + const projectedVolumeName = "my-projected"; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: projectedVolumeName, + projected: projectedVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.baseElement).toMatchSnapshot(); + expect(result.getByText("foo⇢/bar", { exact: false })).toBeTruthy(); + }); + + it("renders a secret source including overriding mode", () => { + const projectedVolume: ProjectedSource = { + defaultMode: 0o777, + sources: [{ + secret: { + name: "my-projected-secret", + items: [{ + key: "foo", + path: "/bar", + mode: 0o666, + }], + }, + }], + }; + const projectedVolumeName = "my-projected"; + const pod = new Pod({ + apiVersion: "v1", + kind: "Pod", + metadata: { + name: "my-pod", + namespace: "default", + resourceVersion: "1", + uid: "123", + selfLink: "/api/v1/pod/default/my-pod", + }, + spec: { + volumes: [{ + name: projectedVolumeName, + projected: projectedVolume, + }], + }, + }); + const result = render(( + + )); + + expect(result.baseElement).toMatchSnapshot(); + expect(result.getByText("(0o666)", { exact: false })).toBeTruthy(); + }); +}); diff --git a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx b/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx index 8b04d61755..430bcd4ad1 100644 --- a/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx +++ b/src/renderer/components/+workloads-pods/details/volumes/variants/projected.tsx @@ -4,18 +4,21 @@ */ import React from "react"; +import { displayMode } from "../../../../../utils"; import { DrawerItem, DrawerTitle } from "../../../../drawer"; import type { VolumeVariantComponent } from "../variant-helpers"; export const Projected: VolumeVariantComponent<"projected"> = ( ({ variant: { sources, defaultMode }}) => ( <> - - 0o{defaultMode.toString(8)} - + {typeof defaultMode === "number" && ( + + {displayMode(defaultMode)} + + )} { - sources.map(({ secret, downwardAPI, configMap, serviceAccountToken }, index) => ( + sources?.map(({ secret, downwardAPI, configMap, serviceAccountToken }, index) => ( {secret && ( <> @@ -25,9 +28,12 @@ export const Projected: VolumeVariantComponent<"projected"> = (
      - {secret.items?.map(({ key, path }) => ( + {secret.items?.map(({ key, path, mode }) => (
    • - {key} ⇢ {path} + {`${key}⇢${path}`} + {typeof mode === "number" && ( + ` (${displayMode(mode)})` + )}
    • ))}
    diff --git a/src/renderer/components/monaco-editor/monaco-editor.tsx b/src/renderer/components/monaco-editor/monaco-editor.tsx index 6c8f3e01a6..f68f8b2ad0 100644 --- a/src/renderer/components/monaco-editor/monaco-editor.tsx +++ b/src/renderer/components/monaco-editor/monaco-editor.tsx @@ -14,7 +14,6 @@ import { debounce, merge } from "lodash"; import { autoBind, cssNames, disposer } from "../../utils"; import { UserStore } from "../../../common/user-store"; import { ThemeStore } from "../../theme.store"; -import logger from "../../../main/logger"; export type MonacoEditorId = string; @@ -60,6 +59,12 @@ export class MonacoEditor extends React.Component { @observable readonly dimensions: { width?: number; height?: number } = {}; @observable private unmounting = false; + + // TODO: investigate how to replace with "common/logger" + // currently leads for stucking UI forever & infinite loop. + // e.g. happens on tab change/create, maybe some other cases too. + private logger = console; + constructor(props: MonacoEditorProps) { super(props); makeObservable(this); @@ -119,7 +124,7 @@ export class MonacoEditor extends React.Component { } protected onModelChange(model: editor.ITextModel, oldModel?: editor.ITextModel) { - logger.info("[MONACO]: model change", { model, oldModel }, this.logMetadata); + this.logger.info("[MONACO]: model change", { model, oldModel }, this.logMetadata); if (oldModel) { this.saveViewState(oldModel); @@ -152,9 +157,9 @@ export class MonacoEditor extends React.Component { componentDidMount() { try { this.createEditor(); - logger.info(`[MONACO]: editor did mount`, this.logMetadata); + this.logger.info(`[MONACO]: editor did mount`, this.logMetadata); } catch (error) { - logger.error(`[MONACO]: mounting failed: ${error}`, this.logMetadata); + this.logger.error(`[MONACO]: mounting failed: ${error}`, this.logMetadata); } } @@ -180,7 +185,7 @@ export class MonacoEditor extends React.Component { ...this.options, }); - logger.info(`[MONACO]: editor created for language=${language}, theme=${theme}`, this.logMetadata); + this.logger.info(`[MONACO]: editor created for language=${language}, theme=${theme}`, this.logMetadata); this.validateLazy(); // validate initial value this.restoreViewState(this.model); // restore previous state if any diff --git a/src/renderer/utils/display-mode.ts b/src/renderer/utils/display-mode.ts new file mode 100644 index 0000000000..e0d2be5d88 --- /dev/null +++ b/src/renderer/utils/display-mode.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +/** + * Format `mode` in octal notation + */ +export function displayMode(mode: number): string { + return `0o${mode.toString(8)}`; +} diff --git a/src/renderer/utils/index.ts b/src/renderer/utils/index.ts index 8f41c54e57..fee7dfc56d 100755 --- a/src/renderer/utils/index.ts +++ b/src/renderer/utils/index.ts @@ -10,6 +10,7 @@ export * from "../../common/event-emitter"; export * from "./cssNames"; export * from "./cssVar"; export * from "./display-booleans"; +export * from "./display-mode"; export * from "./interval"; export * from "./isMiddleClick"; export * from "./isReactNode";