diff --git a/src/common/k8s-api/endpoints/pod.api.ts b/src/common/k8s-api/endpoints/pod.api.ts
index 7b0bf55e9e..8e3aa51559 100644
--- a/src/common/k8s-api/endpoints/pod.api.ts
+++ b/src/common/k8s-api/endpoints/pod.api.ts
@@ -445,46 +445,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/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`] = `
+
+
+
+`;
+
+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
+
+
+
+
+
+
+
+`;
+
+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/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 cd725f1c67..c7ca22c87f 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}`}
+ {typeof mode === "number" && (
+ ` (${displayMode(mode)})`
+ )}
))}
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";