mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix crash in ProjectedVolume component (#5467)
This commit is contained in:
parent
2714941c9e
commit
cf72af3263
@ -432,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 {
|
||||
|
||||
@ -0,0 +1,229 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Projected /> renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Sources
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<Projected /> renders a secret source including overriding mode 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Default Mount Mode
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
0o777
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Sources
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="DrawerTitle sub-title"
|
||||
>
|
||||
Secret
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Name
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
my-projected-secret
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Items
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
foo⇢/bar
|
||||
(0o666)
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<Projected /> renders a secret source, when provided 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Default Mount Mode
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
0o777
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Sources
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="DrawerTitle sub-title"
|
||||
>
|
||||
Secret
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Name
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
my-projected-secret
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Items
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
foo⇢/bar
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<Projected /> renders default mount mode in octal when provided 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Default Mount Mode
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
0o777
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Sources
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<Projected /> renders when no sources array provided 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Default Mount Mode
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
0o777
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="DrawerItem"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Sources
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -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("<Projected />", () => {
|
||||
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((
|
||||
<Projected
|
||||
pod={pod}
|
||||
volumeName={projectedVolumeName}
|
||||
variant={projectedVolume}
|
||||
/>
|
||||
));
|
||||
|
||||
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((
|
||||
<Projected
|
||||
pod={pod}
|
||||
volumeName={projectedVolumeName}
|
||||
variant={projectedVolume}
|
||||
/>
|
||||
));
|
||||
|
||||
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((
|
||||
<Projected
|
||||
pod={pod}
|
||||
volumeName={projectedVolumeName}
|
||||
variant={projectedVolume}
|
||||
/>
|
||||
));
|
||||
|
||||
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((
|
||||
<Projected
|
||||
pod={pod}
|
||||
volumeName={projectedVolumeName}
|
||||
variant={projectedVolume}
|
||||
/>
|
||||
));
|
||||
|
||||
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((
|
||||
<Projected
|
||||
pod={pod}
|
||||
volumeName={projectedVolumeName}
|
||||
variant={projectedVolume}
|
||||
/>
|
||||
));
|
||||
|
||||
expect(result.baseElement).toMatchSnapshot();
|
||||
expect(result.getByText("(0o666)", { exact: false })).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -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 }}) => (
|
||||
<>
|
||||
<DrawerItem name="Default Mount Mode">
|
||||
0o{defaultMode.toString(8)}
|
||||
</DrawerItem>
|
||||
{typeof defaultMode === "number" && (
|
||||
<DrawerItem name="Default Mount Mode">
|
||||
{displayMode(defaultMode)}
|
||||
</DrawerItem>
|
||||
)}
|
||||
<DrawerItem name="Sources">
|
||||
{
|
||||
sources.map(({ secret, downwardAPI, configMap, serviceAccountToken }, index) => (
|
||||
sources?.map(({ secret, downwardAPI, configMap, serviceAccountToken }, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{secret && (
|
||||
<>
|
||||
@ -25,9 +28,12 @@ export const Projected: VolumeVariantComponent<"projected"> = (
|
||||
</DrawerItem>
|
||||
<DrawerItem name="Items">
|
||||
<ul>
|
||||
{secret.items?.map(({ key, path }) => (
|
||||
{secret.items?.map(({ key, path, mode }) => (
|
||||
<li key={key}>
|
||||
{key} ⇢ {path}
|
||||
{`${key}⇢${path}`}
|
||||
{typeof mode === "number" && (
|
||||
` (${displayMode(mode)})`
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
11
src/renderer/utils/display-mode.ts
Normal file
11
src/renderer/utils/display-mode.ts
Normal file
@ -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)}`;
|
||||
}
|
||||
@ -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";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user