mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fully support displaying all supporter PodVolume types
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
a76fc6df84
commit
2b2395d4dd
@ -141,8 +141,11 @@ export function isMetricsEmpty(metrics: Record<string, IMetrics>) {
|
|||||||
return Object.values(metrics).every(metric => !metric?.data?.result?.length);
|
return Object.values(metrics).every(metric => !metric?.data?.result?.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getItemMetrics(metrics: Record<string, IMetrics>, itemName: string): Record<string, IMetrics> | void {
|
export function getItemMetrics(metrics: Record<string, IMetrics>, itemName: string): Record<string, IMetrics> | undefined {
|
||||||
if (!metrics) return;
|
if (!metrics) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const itemMetrics = { ...metrics };
|
const itemMetrics = { ...metrics };
|
||||||
|
|
||||||
for (const metric in metrics) {
|
for (const metric in metrics) {
|
||||||
|
|||||||
@ -31,17 +31,25 @@ export interface IPvcMetrics<T = IMetrics> {
|
|||||||
diskCapacity: T;
|
diskCapacity: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PersistentVolumeClaim {
|
export interface PersistentVolumeClaimSpec {
|
||||||
spec: {
|
accessModes: string[];
|
||||||
accessModes: string[];
|
selector: LabelSelector;
|
||||||
storageClassName: string;
|
resources: {
|
||||||
selector: LabelSelector;
|
requests?: Record<string, string>;
|
||||||
resources: {
|
limits?: Record<string, string>;
|
||||||
requests: {
|
|
||||||
storage: string; // 8Gi
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
volumeName?: string;
|
||||||
|
storageClassName?: string;
|
||||||
|
volumeMode?: string;
|
||||||
|
dataSource?: {
|
||||||
|
apiGroup: string;
|
||||||
|
kind: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersistentVolumeClaim {
|
||||||
|
spec: PersistentVolumeClaimSpec;
|
||||||
status: {
|
status: {
|
||||||
phase: string; // Pending
|
phase: string; // Pending
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,6 +9,10 @@ import { IMetrics, metricsApi } from "./metrics.api";
|
|||||||
import { KubeApi } from "../kube-api";
|
import { KubeApi } from "../kube-api";
|
||||||
import type { KubeJsonApiData } from "../kube-json-api";
|
import type { KubeJsonApiData } from "../kube-json-api";
|
||||||
import { isClusterPageContext } from "../../utils/cluster-id-url-parsing";
|
import { isClusterPageContext } from "../../utils/cluster-id-url-parsing";
|
||||||
|
import type { RequireExactlyOne } from "type-fest";
|
||||||
|
import type { KubeObjectMetadata, LocalObjectReference } from "../kube-object";
|
||||||
|
import type { SecretReference } from "./secret.api";
|
||||||
|
import type { PersistentVolumeClaimSpec } from "./persistent-volume-claims.api";
|
||||||
|
|
||||||
export class PodsApi extends KubeApi<Pod> {
|
export class PodsApi extends KubeApi<Pod> {
|
||||||
getLogs = async (params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise<string> => {
|
getLogs = async (params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise<string> => {
|
||||||
@ -112,12 +116,8 @@ export interface IPodContainer extends Partial<Record<PodContainerProbe, IContai
|
|||||||
};
|
};
|
||||||
}[];
|
}[];
|
||||||
envFrom?: {
|
envFrom?: {
|
||||||
configMapRef?: {
|
configMapRef?: LocalObjectReference;
|
||||||
name: string;
|
secretRef?: LocalObjectReference;
|
||||||
};
|
|
||||||
secretRef?: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
}[];
|
}[];
|
||||||
volumeMounts?: {
|
volumeMounts?: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -195,6 +195,444 @@ export interface IPodContainerStatus {
|
|||||||
started?: boolean;
|
started?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AwsElasticBlockStoreSource {
|
||||||
|
volumeID: string;
|
||||||
|
fsType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureDiskSource {
|
||||||
|
/**
|
||||||
|
* The name of the VHD blob object OR the name of an Azure managed data disk if `kind` is `"Managed"`.
|
||||||
|
*/
|
||||||
|
diskName: string;
|
||||||
|
/**
|
||||||
|
* The URI of the vhd blob object OR the `resourceID` of an Azure managed data disk if `kind` is `"Managed"`.
|
||||||
|
*/
|
||||||
|
diskURI: string;
|
||||||
|
/**
|
||||||
|
* Kind of disk
|
||||||
|
* @default "Shared"
|
||||||
|
*/
|
||||||
|
kind?: "Shared" | "Dedicated" | "Managed";
|
||||||
|
/**
|
||||||
|
* Disk caching mode.
|
||||||
|
* @default "None"
|
||||||
|
*/
|
||||||
|
cachingMode?: "None" | "ReadOnly" | "ReadWrite";
|
||||||
|
/**
|
||||||
|
* The filesystem type to mount.
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
/**
|
||||||
|
* Whether the filesystem is used as readOnly.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readonly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureFileSource {
|
||||||
|
/**
|
||||||
|
* The name of the secret that contains both Azure storage account name and key.
|
||||||
|
*/
|
||||||
|
secretName: string;
|
||||||
|
/**
|
||||||
|
* The share name to be used.
|
||||||
|
*/
|
||||||
|
shareName: string;
|
||||||
|
/**
|
||||||
|
* In case the secret is stored in a different namespace.
|
||||||
|
* @default "default"
|
||||||
|
*/
|
||||||
|
secretNamespace?: string;
|
||||||
|
/**
|
||||||
|
* Whether the filesystem is used as readOnly.
|
||||||
|
*/
|
||||||
|
readOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CephfsSource {
|
||||||
|
/**
|
||||||
|
* List of Ceph monitors
|
||||||
|
*/
|
||||||
|
monitors: string[];
|
||||||
|
/**
|
||||||
|
* Used as the mounted root, rather than the full Ceph tree.
|
||||||
|
* @default "/"
|
||||||
|
*/
|
||||||
|
path?: string;
|
||||||
|
/**
|
||||||
|
* The RADOS user name.
|
||||||
|
* @default "admin"
|
||||||
|
*/
|
||||||
|
user?: string;
|
||||||
|
/**
|
||||||
|
* The path to the keyring file.
|
||||||
|
* @default "/etc/ceph/user.secret"
|
||||||
|
*/
|
||||||
|
secretFile?: string;
|
||||||
|
/**
|
||||||
|
* Reference to Ceph authentication secrets. If provided, then the secret overrides `secretFile`
|
||||||
|
*/
|
||||||
|
secretRef?: SecretReference;
|
||||||
|
/**
|
||||||
|
* Whether the filesystem is used as readOnly.
|
||||||
|
*/
|
||||||
|
readOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CinderSource {
|
||||||
|
volumeID: string;
|
||||||
|
fsType: string;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
secretRef?: SecretReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigMapSource {
|
||||||
|
name: string;
|
||||||
|
items: {
|
||||||
|
key: string;
|
||||||
|
path: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DownwardApiSource {
|
||||||
|
items: {
|
||||||
|
path: string;
|
||||||
|
fieldRef: {
|
||||||
|
fieldPath: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EphemeralSource {
|
||||||
|
volumeClaimTemplate: {
|
||||||
|
/**
|
||||||
|
* All the rest of the fields are ignored and rejected during validation
|
||||||
|
*/
|
||||||
|
metadata?: Pick<KubeObjectMetadata, "labels" | "annotations">;
|
||||||
|
spec: PersistentVolumeClaimSpec;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EmptyDirSource {
|
||||||
|
medium?: string;
|
||||||
|
sizeLimit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FiberChannelSource {
|
||||||
|
/**
|
||||||
|
* A list of World Wide Names
|
||||||
|
*/
|
||||||
|
targetWWNs: string[];
|
||||||
|
/**
|
||||||
|
* Logical Unit number
|
||||||
|
*/
|
||||||
|
lun: number;
|
||||||
|
/**
|
||||||
|
* The type of filesystem
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
readOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlokerSource {
|
||||||
|
datasetName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlexVolumeSource {
|
||||||
|
driver: string;
|
||||||
|
fsType?: string;
|
||||||
|
secretRef?: LocalObjectReference;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
options?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GcePersistentDiskSource {
|
||||||
|
pdName: string;
|
||||||
|
fsType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GitRepoSource {
|
||||||
|
repository: string;
|
||||||
|
revision: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GlusterFsSource {
|
||||||
|
/**
|
||||||
|
* The name of the Endpoints object that represents a Gluster cluster configuration.
|
||||||
|
*/
|
||||||
|
endpoints: string;
|
||||||
|
/**
|
||||||
|
* The Glusterfs volume name.
|
||||||
|
*/
|
||||||
|
path: string;
|
||||||
|
/**
|
||||||
|
* The boolean that sets the mountpoint readOnly or readWrite.
|
||||||
|
*/
|
||||||
|
readOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HostPathSource {
|
||||||
|
path: string;
|
||||||
|
/**
|
||||||
|
* Determines the sorts of checks that will be done
|
||||||
|
* @default ""
|
||||||
|
*/
|
||||||
|
type?: "" | "DirectoryOrCreate" | "Directory" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IscsiSource {
|
||||||
|
targetPortal: string;
|
||||||
|
iqn: string;
|
||||||
|
lun: number;
|
||||||
|
fsType: string;
|
||||||
|
readOnly: boolean;
|
||||||
|
chapAuthDiscovery?: boolean;
|
||||||
|
chapAuthSession?: boolean;
|
||||||
|
secretRef?: SecretReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LocalSource {
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NetworkFsSource {
|
||||||
|
server: string;
|
||||||
|
path: string;
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PVCSource {
|
||||||
|
claimName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhotonPersistentDiskSource {
|
||||||
|
pdID: string;
|
||||||
|
/**
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PortworxVolumeSource {
|
||||||
|
volumeID: string;
|
||||||
|
fsType?: string;
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuobyteSource {
|
||||||
|
registry: string;
|
||||||
|
volume: string;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
/**
|
||||||
|
* @default "serivceaccount"
|
||||||
|
*/
|
||||||
|
user?: string;
|
||||||
|
group?: string;
|
||||||
|
tenant?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RbdSource {
|
||||||
|
monitors: string[];
|
||||||
|
image: string;
|
||||||
|
/**
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
/**
|
||||||
|
* @default "rbd"
|
||||||
|
*/
|
||||||
|
pool?: string;
|
||||||
|
/**
|
||||||
|
* @default "admin"
|
||||||
|
*/
|
||||||
|
user?: string;
|
||||||
|
/**
|
||||||
|
* @default "/etc/ceph/keyring"
|
||||||
|
*/
|
||||||
|
keyring?: string;
|
||||||
|
secretRef?: SecretReference;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScaleIoSource {
|
||||||
|
gateway: string;
|
||||||
|
system: string;
|
||||||
|
secretRef?: LocalObjectReference;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
sslEnabled?: boolean;
|
||||||
|
protectionDomain?: string;
|
||||||
|
storagePool?: string;
|
||||||
|
/**
|
||||||
|
* @default "ThinProvisioned"
|
||||||
|
*/
|
||||||
|
storageMode?: "ThickProvisioned" | "ThinProvisioned";
|
||||||
|
volumeName: string;
|
||||||
|
/**
|
||||||
|
* @default "xfs"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SecretSource {
|
||||||
|
secretName: string;
|
||||||
|
items?: {
|
||||||
|
key: string;
|
||||||
|
path: string;
|
||||||
|
mode?: number;
|
||||||
|
}[];
|
||||||
|
defaultMode?: number;
|
||||||
|
optional?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StorageOsSource {
|
||||||
|
volumeName: string;
|
||||||
|
/**
|
||||||
|
* @default Pod.metadata.namespace
|
||||||
|
*/
|
||||||
|
volumeNamespace?: string;
|
||||||
|
/**
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
secretRef?: LocalObjectReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VsphereVolumeSource {
|
||||||
|
volumePath: string;
|
||||||
|
/**
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
storagePolicyName?: string;
|
||||||
|
storagePolicyID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContainerStorageInterfaceSource {
|
||||||
|
driver: string;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean;
|
||||||
|
/**
|
||||||
|
* @default "ext4"
|
||||||
|
*/
|
||||||
|
fsType?: string;
|
||||||
|
volumeAttributes?: Record<string, string>;
|
||||||
|
controllerPublishSecretRef?: SecretReference;
|
||||||
|
nodeStageSecretRef?: SecretReference;
|
||||||
|
nodePublishSecretRef?: SecretReference;
|
||||||
|
controllerExpandSecretRef?: SecretReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PodVolumeVariants {
|
||||||
|
awsElasticBlockStore: AwsElasticBlockStoreSource;
|
||||||
|
azureDisk: AzureDiskSource;
|
||||||
|
azureFile: AzureFileSource;
|
||||||
|
cephfs: CephfsSource;
|
||||||
|
cinder: CinderSource;
|
||||||
|
configMap: ConfigMapSource;
|
||||||
|
downwardAPI: DownwardApiSource;
|
||||||
|
ephemeral: EphemeralSource;
|
||||||
|
emptyDir: EmptyDirSource;
|
||||||
|
fc: FiberChannelSource;
|
||||||
|
flocker: FlokerSource;
|
||||||
|
flexVolume: FlexVolumeSource;
|
||||||
|
gcePersistentDisk: GcePersistentDiskSource;
|
||||||
|
gitRepo: GitRepoSource;
|
||||||
|
glusterfs: GlusterFsSource;
|
||||||
|
hostPath: HostPathSource;
|
||||||
|
iscsi: IscsiSource;
|
||||||
|
local: LocalSource;
|
||||||
|
nfs: NetworkFsSource;
|
||||||
|
persistentVolumeClaim: PVCSource;
|
||||||
|
photonPersistentDisk: PhotonPersistentDiskSource;
|
||||||
|
portworxVolume: PortworxVolumeSource;
|
||||||
|
projected: ProjectedSource;
|
||||||
|
quobyte: QuobyteSource;
|
||||||
|
rbd: RbdSource;
|
||||||
|
scaleIO: ScaleIoSource;
|
||||||
|
secret: SecretSource;
|
||||||
|
storageos: StorageOsSource;
|
||||||
|
vsphereVolume: VsphereVolumeSource;
|
||||||
|
csi: ContainerStorageInterfaceSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The valid kinds of volume
|
||||||
|
*/
|
||||||
|
export type PodVolumeKind = keyof PodVolumeVariants;
|
||||||
|
|
||||||
|
export type PodVolume = RequireExactlyOne<PodVolumeVariants> & {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
export class Pod extends WorkloadKubeObject {
|
export class Pod extends WorkloadKubeObject {
|
||||||
static kind = "Pod";
|
static kind = "Pod";
|
||||||
static namespaced = true;
|
static namespaced = true;
|
||||||
@ -206,23 +644,7 @@ export class Pod extends WorkloadKubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare spec?: {
|
declare spec?: {
|
||||||
volumes?: {
|
volumes?: PodVolume[];
|
||||||
name: string;
|
|
||||||
persistentVolumeClaim: {
|
|
||||||
claimName: string;
|
|
||||||
};
|
|
||||||
emptyDir: {
|
|
||||||
medium?: string;
|
|
||||||
sizeLimit?: string;
|
|
||||||
};
|
|
||||||
configMap: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
secret: {
|
|
||||||
secretName: string;
|
|
||||||
defaultMode: number;
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
initContainers: IPodContainer[];
|
initContainers: IPodContainer[];
|
||||||
containers: IPodContainer[];
|
containers: IPodContainer[];
|
||||||
restartPolicy?: string;
|
restartPolicy?: string;
|
||||||
@ -239,9 +661,7 @@ export class Pod extends WorkloadKubeObject {
|
|||||||
[selector: string]: string;
|
[selector: string]: string;
|
||||||
};
|
};
|
||||||
securityContext?: {};
|
securityContext?: {};
|
||||||
imagePullSecrets?: {
|
imagePullSecrets?: LocalObjectReference[];
|
||||||
name: string;
|
|
||||||
}[];
|
|
||||||
hostNetwork?: boolean;
|
hostNetwork?: boolean;
|
||||||
hostPID?: boolean;
|
hostPID?: boolean;
|
||||||
hostIPC?: boolean;
|
hostIPC?: boolean;
|
||||||
|
|||||||
@ -25,6 +25,11 @@ export interface ISecretRef {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SecretReference {
|
||||||
|
name: string;
|
||||||
|
namespace?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SecretData extends KubeJsonApiData {
|
export interface SecretData extends KubeJsonApiData {
|
||||||
type: SecretType;
|
type: SecretType;
|
||||||
data?: Record<string, string>;
|
data?: Record<string, string>;
|
||||||
|
|||||||
@ -21,6 +21,13 @@ export type KubeObjectConstructor<K extends KubeObject> = (new (data: KubeJsonAp
|
|||||||
apiBase?: string;
|
apiBase?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to an object in the same namespace
|
||||||
|
*/
|
||||||
|
export interface LocalObjectReference {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface KubeObjectMetadata {
|
export interface KubeObjectMetadata {
|
||||||
uid: string;
|
uid: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
import "./pod-details-secrets.scss";
|
import "./pod-details-secrets.scss";
|
||||||
|
|
||||||
import React, { Component } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { autorun, observable, makeObservable } from "mobx";
|
import { autorun, observable } from "mobx";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Pod, Secret, secretsApi } from "../../../common/k8s-api/endpoints";
|
import { Pod, Secret, secretsApi } from "../../../common/k8s-api/endpoints";
|
||||||
import { getDetailsUrl } from "../kube-detail-params";
|
import { getDetailsUrl } from "../kube-detail-params";
|
||||||
|
|
||||||
@ -16,59 +16,38 @@ export interface PodDetailsSecretsProps {
|
|||||||
pod: Pod;
|
pod: Pod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
export const PodDetailsSecrets = observer(({ pod }: PodDetailsSecretsProps) => {
|
||||||
export class PodDetailsSecrets extends Component<PodDetailsSecretsProps> {
|
const [secrets] = useState(observable.map<string, Secret>());
|
||||||
@observable secrets: Map<string, Secret> = observable.map<string, Secret>();
|
|
||||||
|
|
||||||
componentDidMount(): void {
|
useEffect(() => autorun(async () => {
|
||||||
disposeOnUnmount(this, [
|
const podSecrets = await Promise.all(
|
||||||
autorun(async () => {
|
pod.getSecrets().map(secretName => secretsApi.get({
|
||||||
const { pod } = this.props;
|
name: secretName,
|
||||||
|
namespace: pod.getNs(),
|
||||||
const secrets = await Promise.all(
|
})),
|
||||||
pod.getSecrets().map(secretName => secretsApi.get({
|
|
||||||
name: secretName,
|
|
||||||
namespace: pod.getNs(),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
secrets.forEach(secret => secret && this.secrets.set(secret.getName(), secret));
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props: PodDetailsSecretsProps) {
|
|
||||||
super(props);
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { pod } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="PodDetailsSecrets">
|
|
||||||
{
|
|
||||||
pod.getSecrets().map(secretName => {
|
|
||||||
const secret = this.secrets.get(secretName);
|
|
||||||
|
|
||||||
if (secret) {
|
|
||||||
return this.renderSecretLink(secret);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<span key={secretName}>{secretName}</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
protected renderSecretLink(secret: Secret) {
|
secrets.replace(podSecrets.filter(Boolean).map(secret => [secret.getName(), secret]));
|
||||||
|
}), []);
|
||||||
|
|
||||||
|
const renderSecret = (name: string) => {
|
||||||
|
const secret = secrets.get(name);
|
||||||
|
|
||||||
|
if (!secret) {
|
||||||
|
return <span key={name}>{name}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link key={secret.getId()} to={getDetailsUrl(secret.selfLink)}>
|
<Link key={secret.getId()} to={getDetailsUrl(secret.selfLink)}>
|
||||||
{secret.getName()}
|
{secret.getName()}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
return (
|
||||||
|
<div className="PodDetailsSecrets">
|
||||||
|
{pod.getSecrets().map(renderSecret)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
647
src/renderer/components/+workloads-pods/pod-details-volumes.tsx
Normal file
647
src/renderer/components/+workloads-pods/pod-details-volumes.tsx
Normal file
@ -0,0 +1,647 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jsyaml from "js-yaml";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { configMapApi, Pod, PodVolume, PodVolumeKind, PodVolumeVariants, pvcApi, SecretReference, secretsApi } from "../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeApi } from "../../../common/k8s-api/kube-api";
|
||||||
|
import type { KubeObject, LocalObjectReference } from "../../../common/k8s-api/kube-object";
|
||||||
|
import { DrawerItem, DrawerItemLabels, DrawerSection, DrawerSubSection } from "../drawer";
|
||||||
|
import { Icon } from "../icon";
|
||||||
|
import { getDetailsUrl } from "../kube-detail-params";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional parameters for each volume variant
|
||||||
|
*/
|
||||||
|
export interface VariantOptions {
|
||||||
|
pod: Pod;
|
||||||
|
volumeName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLocalRef(pod: Pod, title: string, ref: LocalObjectReference | SecretReference, typeApi: KubeApi<KubeObject>) {
|
||||||
|
return (
|
||||||
|
<DrawerItem name={title}>
|
||||||
|
<Link to={getDetailsUrl(typeApi.getUrl({ namespace: pod.getNs(), ...ref }))}>
|
||||||
|
{ref.name}
|
||||||
|
</Link>
|
||||||
|
</DrawerItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
type PodVolumeVariantRenderers = {
|
||||||
|
[Key in keyof PodVolumeVariants]: (variant: PodVolumeVariants[Key], opts: VariantOptions) => React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const volumeRenderers: PodVolumeVariantRenderers = {
|
||||||
|
awsElasticBlockStore: ({ volumeID, fsType = "ext4" }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Volume ID">
|
||||||
|
{volumeID}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
azureDisk: ({ diskName, diskURI, kind = "Shared", cachingMode = "None", fsType = "ext4", readonly = false }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name={kind === "Managed" ? "Disk Name" : "VHD blob Name"}>
|
||||||
|
{diskName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name={kind === "Managed" ? "Resource ID" : "Disk URI"}>
|
||||||
|
{diskURI}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Kind">
|
||||||
|
{kind}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Caching Mode">
|
||||||
|
{cachingMode}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readonly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
azureFile: ({ readOnly = false, secretName, shareName, secretNamespace = "default" }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Secret Name">
|
||||||
|
{secretName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Share Name">
|
||||||
|
{shareName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Namespace of Secret">
|
||||||
|
{secretNamespace}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
ephemeral: ({ volumeClaimTemplate: { metadata, spec }}, { pod, volumeName }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="PVC Template Name">
|
||||||
|
{pod.getName()}-{volumeName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItemLabels
|
||||||
|
name="Template Labels"
|
||||||
|
labels={metadata.labels}
|
||||||
|
/>
|
||||||
|
<DrawerItemLabels
|
||||||
|
name="Template Annotations"
|
||||||
|
labels={metadata.annotations}
|
||||||
|
/>
|
||||||
|
<DrawerItem name="Template PVC Spec">
|
||||||
|
{jsyaml.dump(spec)}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
emptyDir: ({ medium, sizeLimit }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Medium" hidden={!medium}>
|
||||||
|
{medium}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Size Limit" hidden={!sizeLimit}>
|
||||||
|
{sizeLimit}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
cephfs: ({ monitors, path = "/", user = "admin", secretFile = "/etc/ceph/user.secret", secretRef, readOnly }, { pod }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Monitors">
|
||||||
|
<ul>
|
||||||
|
{monitors.map(monitor => <li key={monitor}>{monitor}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Mount Path">
|
||||||
|
{path}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Username">
|
||||||
|
{user}
|
||||||
|
</DrawerItem>
|
||||||
|
{
|
||||||
|
secretRef
|
||||||
|
? renderLocalRef(pod, "Secret", secretRef, secretsApi)
|
||||||
|
: (
|
||||||
|
<DrawerItem name="Secret Filepath">
|
||||||
|
{secretFile}
|
||||||
|
</DrawerItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
cinder: ({ volumeID, fsType = "ext4" }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Volume ID">
|
||||||
|
{volumeID}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
configMap: ({ name }, { pod }) => renderLocalRef(pod, "Name", { name }, configMapApi),
|
||||||
|
downwardAPI: ({ items }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Items">
|
||||||
|
<ul>
|
||||||
|
{items.map(item => <li key={item.path}>{item.path}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
fc: ({ targetWWNs, lun, fsType = "ext4", readOnly = false }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Target World Wide Names">
|
||||||
|
<ul>
|
||||||
|
{targetWWNs.map(targetWWN => <li key={targetWWN}>{targetWWN}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Logical Unit Number">
|
||||||
|
{lun.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
flexVolume: ({ driver, fsType, secretRef, readOnly = false, options }, { pod }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Driver">
|
||||||
|
{driver}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType || "-- system default --"}
|
||||||
|
</DrawerItem>
|
||||||
|
{secretRef && renderLocalRef(pod, "Secret", secretRef, secretsApi)}
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
{
|
||||||
|
...Object.entries(options)
|
||||||
|
.map(([key, value]) => (
|
||||||
|
<DrawerItem key={key} name={`Option: ${key}`}>
|
||||||
|
{value}
|
||||||
|
</DrawerItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
flocker: ({ datasetName }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Dataset Name">
|
||||||
|
{datasetName}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
gcePersistentDisk: ({ pdName, fsType = "ext4" }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Persistent Disk Name">
|
||||||
|
{pdName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
gitRepo: ({ repository, revision }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Repository URL">
|
||||||
|
{repository}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Commit Hash">
|
||||||
|
{revision}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
glusterfs: ({ endpoints, path, readOnly = false }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Endpoints object name">
|
||||||
|
{endpoints}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Glusterfs volume name">
|
||||||
|
{path}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly Mountpoint">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
hostPath: ({ path, type }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Node's Host Filesystem Path">
|
||||||
|
{path}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Check Behaviour">
|
||||||
|
{type || "-- none --"}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
iscsi: ({ targetPortal, iqn, lun, fsType = "ext4", readOnly = false, chapAuthDiscovery, chapAuthSession, secretRef }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Target Address">
|
||||||
|
{targetPortal}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="iSCSI qualified name">
|
||||||
|
{iqn}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Logical Unit Number">
|
||||||
|
{lun.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
{chapAuthDiscovery && (
|
||||||
|
<DrawerItem name="CHAP Discovery Authentication">
|
||||||
|
{chapAuthDiscovery.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
|
{chapAuthSession && (
|
||||||
|
<DrawerItem name="CHAP Session Authentication">
|
||||||
|
{chapAuthSession.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
|
{ secretRef && (
|
||||||
|
<DrawerItem name="CHAP Secret">
|
||||||
|
{secretRef.name}
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
local: ({ path }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Path">
|
||||||
|
{path}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
nfs: ({ server, path, readOnly = false }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Server">
|
||||||
|
{server}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Path">
|
||||||
|
{path}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
persistentVolumeClaim: ({ claimName }, { pod }) => renderLocalRef(pod, "Name", { name: claimName }, pvcApi),
|
||||||
|
photonPersistentDisk: ({ pdID, fsType = "ext4" }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Persistent Disk ID">
|
||||||
|
{pdID}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
portworxVolume: ({ volumeID, fsType = "ext4", readOnly = false }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Volume ID">
|
||||||
|
{volumeID}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
projected: ({ sources, defaultMode }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Default Mount Mode">
|
||||||
|
0o{defaultMode.toString(8)}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Sources">
|
||||||
|
{
|
||||||
|
sources.map(({ secret, downwardAPI, configMap, serviceAccountToken }, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{secret && (
|
||||||
|
<DrawerSubSection title="Secret">
|
||||||
|
<DrawerItem name="Name">
|
||||||
|
{secret.name}
|
||||||
|
</DrawerItem>
|
||||||
|
{secret.items && (
|
||||||
|
<DrawerItem name="Items">
|
||||||
|
<ul>
|
||||||
|
{secret.items.map(({ key }) => <li key={key}>{key}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
|
</DrawerSubSection>
|
||||||
|
)}
|
||||||
|
{downwardAPI && (
|
||||||
|
<DrawerSubSection title="Downward API">
|
||||||
|
{downwardAPI.items && (
|
||||||
|
<DrawerItem name="Items">
|
||||||
|
<ul>
|
||||||
|
{downwardAPI.items.map(({ path }) => <li key={path}>{path}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
|
</DrawerSubSection>
|
||||||
|
)}
|
||||||
|
{configMap && (
|
||||||
|
<DrawerSubSection title="Config Map">
|
||||||
|
<DrawerItem name="Name">
|
||||||
|
{configMap.name}
|
||||||
|
</DrawerItem>
|
||||||
|
{configMap.items && (
|
||||||
|
<DrawerItem name="Items">
|
||||||
|
<ul>
|
||||||
|
{configMap.items.map(({ path }) => <li key={path}>{path}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
|
</DrawerSubSection>
|
||||||
|
)}
|
||||||
|
{serviceAccountToken && (
|
||||||
|
<DrawerSubSection title="Service Account Token">
|
||||||
|
<DrawerItem name="Audience" hidden={!serviceAccountToken.audience}>
|
||||||
|
{serviceAccountToken.audience}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Expiration" hidden={!serviceAccountToken.expirationSeconds}>
|
||||||
|
{serviceAccountToken.expirationSeconds}s
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Path">
|
||||||
|
{serviceAccountToken.path}
|
||||||
|
</DrawerItem>
|
||||||
|
</DrawerSubSection>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
quobyte: ({ registry, volume, readOnly = false, user = "serviceaccount", group, tenant }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Registry">
|
||||||
|
{registry}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Volume">
|
||||||
|
{volume}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="User">
|
||||||
|
{user}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Group">
|
||||||
|
{group ?? "-- no group --"}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Tenant" hidden={!tenant}>
|
||||||
|
{tenant}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
rbd: ({ monitors, image, fsType = "ext4", pool = "rbd", user = "admin", keyring = "/etc/ceph/keyright", secretRef, readOnly = false }, { pod }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Ceph Monitors">
|
||||||
|
<ul>
|
||||||
|
{monitors.map(monitor => <li key={monitor}>{monitor}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Image">
|
||||||
|
{image}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Pool">
|
||||||
|
{pool}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="User">
|
||||||
|
{user}
|
||||||
|
</DrawerItem>
|
||||||
|
{
|
||||||
|
secretRef
|
||||||
|
? renderLocalRef(pod, "Authentication Secret", secretRef, secretsApi)
|
||||||
|
: (
|
||||||
|
<DrawerItem name="Keyright Path">
|
||||||
|
{keyring}
|
||||||
|
</DrawerItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
scaleIO: ({ gateway, system, secretRef, sslEnabled = false, protectionDomain, storagePool, storageMode = "ThinProvisioned", volumeName, fsType = "xfs", readOnly = false }, { pod }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Gateway">
|
||||||
|
{gateway}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="System">
|
||||||
|
{system}
|
||||||
|
</DrawerItem>
|
||||||
|
{renderLocalRef(pod, "Name", secretRef, secretsApi)}
|
||||||
|
<DrawerItem name="SSL Enabled">
|
||||||
|
{sslEnabled.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="SSL Enabled">
|
||||||
|
{sslEnabled.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Protection Domain Name" hidden={!protectionDomain}>
|
||||||
|
{protectionDomain}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Storage Pool" hidden={!storagePool}>
|
||||||
|
{storagePool}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Storage Mode" hidden={!storageMode}>
|
||||||
|
{storageMode}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Volume Name">
|
||||||
|
{volumeName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
secret: ({ secretName, items = [], defaultMode = 0o644, optional = false }, { pod }) => (
|
||||||
|
<>
|
||||||
|
{renderLocalRef(pod, "Name", { name: secretName }, secretsApi)}
|
||||||
|
<DrawerItem name="Items" hidden={items.length === 0}>
|
||||||
|
<ul>
|
||||||
|
{items.map(({ key }) => <li key={key}>{key}</li>)}
|
||||||
|
</ul>
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Default File Mode">
|
||||||
|
0o{defaultMode.toString(8)}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Optional">
|
||||||
|
{optional.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
storageos: ({ volumeName, volumeNamespace, fsType = "ext4", readOnly = false, secretRef }, { pod }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Volume Name">
|
||||||
|
{volumeName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Volume Namespace" hidden={volumeNamespace === "default"}>
|
||||||
|
{
|
||||||
|
volumeNamespace === volumeName
|
||||||
|
? "- no default behaviour -"
|
||||||
|
: volumeNamespace || pod.getNs()
|
||||||
|
}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Readonly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
{secretRef && renderLocalRef(pod, "Secret", secretRef, secretsApi)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
vsphereVolume: ({ volumePath, fsType = "ext4", storagePolicyName, storagePolicyID }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Virtual Machine Disk Volume">
|
||||||
|
{volumePath}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Storage Policy Based Management Profile Name" hidden={!storagePolicyName}>
|
||||||
|
{storagePolicyName}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Storage Policy Based Management Profile ID" hidden={!storagePolicyID}>
|
||||||
|
{storagePolicyID}
|
||||||
|
</DrawerItem>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
csi: ({ driver, readOnly = false, fsType = "ext4", volumeAttributes = {}, nodePublishSecretRef, controllerPublishSecretRef, nodeStageSecretRef, controllerExpandSecretRef }, { pod }) => (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Driver">
|
||||||
|
{driver}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="ReadOnly">
|
||||||
|
{readOnly.toString()}
|
||||||
|
</DrawerItem>
|
||||||
|
<DrawerItem name="Filesystem Type">
|
||||||
|
{fsType}
|
||||||
|
</DrawerItem>
|
||||||
|
{controllerPublishSecretRef && renderLocalRef(pod, "Controller Publish Secret", controllerPublishSecretRef, secretsApi)}
|
||||||
|
{controllerExpandSecretRef && renderLocalRef(pod, "Controller Expand Secret", controllerExpandSecretRef, secretsApi)}
|
||||||
|
{nodePublishSecretRef && renderLocalRef(pod, "Node Publish Secret", nodePublishSecretRef, secretsApi)}
|
||||||
|
{nodeStageSecretRef && renderLocalRef(pod, "Node Stage Secret", nodeStageSecretRef, secretsApi)}
|
||||||
|
{
|
||||||
|
...Object.entries(volumeAttributes)
|
||||||
|
.map(([key, value]) => (
|
||||||
|
<DrawerItem key={key} name={key}>
|
||||||
|
{value}
|
||||||
|
</DrawerItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface PodVolumesProps {
|
||||||
|
pod: Pod;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deprecatedVolumeTypes = new Set<PodVolumeKind>([
|
||||||
|
"flocker",
|
||||||
|
"gitRepo",
|
||||||
|
"quobyte",
|
||||||
|
"storageos",
|
||||||
|
]);
|
||||||
|
|
||||||
|
function getVolumeType(volume: PodVolume): PodVolumeKind | undefined {
|
||||||
|
const keys = new Set(Object.keys(volume));
|
||||||
|
|
||||||
|
keys.delete("name"); // This key is not a kind field
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
// skip other random keys
|
||||||
|
if (key in volumeRenderers) {
|
||||||
|
const kind = key as PodVolumeKind;
|
||||||
|
|
||||||
|
if (volume[kind] && typeof volume[kind] === "object") {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PodVolumes = observer(({ pod }: PodVolumesProps) => {
|
||||||
|
if (!pod) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderVolume = (volume: PodVolume) => {
|
||||||
|
const type = getVolumeType(volume);
|
||||||
|
const isDeprecated = deprecatedVolumeTypes.has(type);
|
||||||
|
const renderVolume = volumeRenderers[type];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={volume.name} className="volume">
|
||||||
|
<div className="title flex gaps">
|
||||||
|
<Icon small material="storage" />
|
||||||
|
<span>{volume.name}</span>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
renderVolume
|
||||||
|
? (
|
||||||
|
<>
|
||||||
|
<DrawerItem name="Type">
|
||||||
|
{type}
|
||||||
|
{isDeprecated && <Icon title="Deprecated" material="warning_amber" />}
|
||||||
|
</DrawerItem>
|
||||||
|
{renderVolume(volume[type] as any, { pod, volumeName: volume.name })}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
: type
|
||||||
|
? (
|
||||||
|
<DrawerItem name="Type">
|
||||||
|
{type}
|
||||||
|
</DrawerItem>
|
||||||
|
)
|
||||||
|
: <p>Error! Unknown pod volume kind</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const volumes = pod.getVolumes();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DrawerSection title="Volumes" hidden={volumes.length === 0}>
|
||||||
|
{volumes.map(renderVolume)}
|
||||||
|
</DrawerSection>
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -10,14 +10,13 @@ import kebabCase from "lodash/kebabCase";
|
|||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { observable, reaction, makeObservable } from "mobx";
|
import { observable, reaction, makeObservable } from "mobx";
|
||||||
import { type IPodMetrics, nodesApi, Pod, pvcApi, configMapApi, getMetricsForPods } from "../../../common/k8s-api/endpoints";
|
import { type IPodMetrics, nodesApi, Pod, getMetricsForPods } from "../../../common/k8s-api/endpoints";
|
||||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
import { DrawerItem, DrawerSection } from "../drawer";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { boundMethod, cssNames, toJS } from "../../utils";
|
import { boundMethod, cssNames, toJS } from "../../utils";
|
||||||
import { PodDetailsContainer } from "./pod-details-container";
|
import { PodDetailsContainer } from "./pod-details-container";
|
||||||
import { PodDetailsAffinities } from "./pod-details-affinities";
|
import { PodDetailsAffinities } from "./pod-details-affinities";
|
||||||
import { PodDetailsTolerations } from "./pod-details-tolerations";
|
import { PodDetailsTolerations } from "./pod-details-tolerations";
|
||||||
import { Icon } from "../icon";
|
|
||||||
import { PodDetailsSecrets } from "./pod-details-secrets";
|
import { PodDetailsSecrets } from "./pod-details-secrets";
|
||||||
import { ResourceMetrics } from "../resource-metrics";
|
import { ResourceMetrics } from "../resource-metrics";
|
||||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||||
@ -28,6 +27,7 @@ import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
|||||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||||
import { getDetailsUrl } from "../kube-detail-params";
|
import { getDetailsUrl } from "../kube-detail-params";
|
||||||
import logger from "../../../common/logger";
|
import logger from "../../../common/logger";
|
||||||
|
import { PodVolumes } from "./pod-details-volumes";
|
||||||
|
|
||||||
export interface PodDetailsProps extends KubeObjectDetailsProps<Pod> {
|
export interface PodDetailsProps extends KubeObjectDetailsProps<Pod> {
|
||||||
}
|
}
|
||||||
@ -73,12 +73,12 @@ export class PodDetails extends React.Component<PodDetailsProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { status, spec } = pod;
|
const { status, spec } = pod;
|
||||||
const { conditions, podIP } = status;
|
const { conditions = [], podIP } = status ?? {};
|
||||||
const podIPs = pod.getIPs();
|
const podIPs = pod.getIPs();
|
||||||
const { nodeName } = spec;
|
const { nodeName } = spec ?? {};
|
||||||
const nodeSelector = pod.getNodeSelectors();
|
const nodeSelector = pod.getNodeSelectors();
|
||||||
const volumes = pod.getVolumes();
|
|
||||||
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Pod);
|
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Pod);
|
||||||
|
const initContainers = pod.getInitContainers();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="PodDetails">
|
<div className="PodDetails">
|
||||||
@ -90,26 +90,22 @@ export class PodDetails extends React.Component<PodDetailsProps> {
|
|||||||
<PodCharts/>
|
<PodCharts/>
|
||||||
</ResourceMetrics>
|
</ResourceMetrics>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<KubeObjectMeta object={pod}/>
|
<KubeObjectMeta object={pod}/>
|
||||||
|
|
||||||
<DrawerItem name="Status">
|
<DrawerItem name="Status">
|
||||||
<span className={cssNames("status", kebabCase(pod.getStatusMessage()))}>{pod.getStatusMessage()}</span>
|
<span className={cssNames("status", kebabCase(pod.getStatusMessage()))}>{pod.getStatusMessage()}</span>
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Node">
|
<DrawerItem name="Node" hidden={!nodeName}>
|
||||||
{nodeName && (
|
<Link to={getDetailsUrl(nodesApi.getUrl({ name: nodeName }))}>
|
||||||
<Link to={getDetailsUrl(nodesApi.getUrl({ name: nodeName }))}>
|
{nodeName}
|
||||||
{nodeName}
|
</Link>
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Pod IP">
|
<DrawerItem name="Pod IP">
|
||||||
{podIP}
|
{podIP}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Pod IPs" hidden={!podIPs.length} labelsOnly>
|
<DrawerItem name="Pod IPs" hidden={podIPs.length === 0} labelsOnly>
|
||||||
{
|
{podIPs.map(label => <Badge key={label} label={label} />)}
|
||||||
podIPs.map(label => (
|
|
||||||
<Badge key={label} label={label}/>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Priority Class">
|
<DrawerItem name="Priority Class">
|
||||||
{pod.getPriorityClassName()}
|
{pod.getPriorityClassName()}
|
||||||
@ -117,129 +113,49 @@ export class PodDetails extends React.Component<PodDetailsProps> {
|
|||||||
<DrawerItem name="QoS Class">
|
<DrawerItem name="QoS Class">
|
||||||
{pod.getQosClass()}
|
{pod.getQosClass()}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
{conditions &&
|
|
||||||
<DrawerItem name="Conditions" className="conditions" labelsOnly>
|
|
||||||
{
|
|
||||||
conditions.map(condition => {
|
|
||||||
const { type, status, lastTransitionTime } = condition;
|
|
||||||
|
|
||||||
return (
|
<DrawerItem name="Conditions" className="conditions" hidden={conditions.length === 0} labelsOnly>
|
||||||
<Badge
|
|
||||||
key={type}
|
|
||||||
label={type}
|
|
||||||
disabled={status === "False"}
|
|
||||||
tooltip={`Last transition time: ${lastTransitionTime}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</DrawerItem>
|
|
||||||
}
|
|
||||||
{nodeSelector.length > 0 &&
|
|
||||||
<DrawerItem name="Node Selector">
|
|
||||||
{
|
{
|
||||||
nodeSelector.map(label => (
|
conditions.map(({ type, status, lastTransitionTime }) => (
|
||||||
<Badge key={label} label={label}/>
|
<Badge
|
||||||
|
key={type}
|
||||||
|
label={type}
|
||||||
|
disabled={status === "False"}
|
||||||
|
tooltip={`Last transition time: ${lastTransitionTime}`}
|
||||||
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
}
|
|
||||||
|
<DrawerItem name="Node Selector" hidden={nodeSelector.length === 0}>
|
||||||
|
{nodeSelector.map(label => <Badge key={label} label={label} />)}
|
||||||
|
</DrawerItem>
|
||||||
|
|
||||||
<PodDetailsTolerations workload={pod}/>
|
<PodDetailsTolerations workload={pod}/>
|
||||||
<PodDetailsAffinities workload={pod}/>
|
<PodDetailsAffinities workload={pod}/>
|
||||||
|
|
||||||
{pod.getSecrets().length > 0 && (
|
<DrawerItem name="Secrets" hidden={pod.getSecrets().length === 0}>
|
||||||
<DrawerItem name="Secrets">
|
<PodDetailsSecrets pod={pod}/>
|
||||||
<PodDetailsSecrets pod={pod}/>
|
</DrawerItem>
|
||||||
</DrawerItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{pod.getInitContainers() && pod.getInitContainers().length > 0 &&
|
<DrawerSection title="Init Containers" hidden={initContainers.length === 0}>
|
||||||
<DrawerTitle title="Init Containers"/>
|
{initContainers.map(c => <PodDetailsContainer key={c.name} pod={pod} container={c} />)}
|
||||||
}
|
</DrawerSection>
|
||||||
{
|
|
||||||
pod.getInitContainers() && pod.getInitContainers().map(container => {
|
|
||||||
return <PodDetailsContainer key={container.name} pod={pod} container={container}/>;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<DrawerTitle title="Containers"/>
|
|
||||||
{
|
|
||||||
pod.getContainers().map(container => {
|
|
||||||
const { name } = container;
|
|
||||||
const metrics = getItemMetrics(toJS(this.containerMetrics), name);
|
|
||||||
|
|
||||||
return (
|
<DrawerSection title="Containers">
|
||||||
|
{
|
||||||
|
pod.getContainers().map(container => (
|
||||||
<PodDetailsContainer
|
<PodDetailsContainer
|
||||||
key={name}
|
key={container.name}
|
||||||
pod={pod}
|
pod={pod}
|
||||||
container={container}
|
container={container}
|
||||||
metrics={metrics || null}
|
metrics={getItemMetrics(toJS(this.containerMetrics), container.name)}
|
||||||
/>
|
/>
|
||||||
);
|
))
|
||||||
})
|
}
|
||||||
}
|
</DrawerSection>
|
||||||
|
|
||||||
{volumes.length > 0 && (
|
<PodVolumes pod={pod} />
|
||||||
<>
|
|
||||||
<DrawerTitle title="Volumes"/>
|
|
||||||
{volumes.map(volume => {
|
|
||||||
const claimName = volume.persistentVolumeClaim ? volume.persistentVolumeClaim.claimName : null;
|
|
||||||
const configMap = volume.configMap ? volume.configMap.name : null;
|
|
||||||
const type = Object.keys(volume)[1];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={volume.name} className="volume">
|
|
||||||
<div className="title flex gaps">
|
|
||||||
<Icon small material="storage"/>
|
|
||||||
<span>{volume.name}</span>
|
|
||||||
</div>
|
|
||||||
<DrawerItem name="Type">
|
|
||||||
{type}
|
|
||||||
</DrawerItem>
|
|
||||||
{ type == "configMap" && (
|
|
||||||
<div>
|
|
||||||
{configMap && (
|
|
||||||
<DrawerItem name="Name">
|
|
||||||
<Link
|
|
||||||
to={getDetailsUrl(configMapApi.getUrl({
|
|
||||||
name: configMap,
|
|
||||||
namespace: pod.getNs(),
|
|
||||||
}))}>{configMap}
|
|
||||||
</Link>
|
|
||||||
</DrawerItem>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{ type === "emptyDir" && (
|
|
||||||
<div>
|
|
||||||
{ volume.emptyDir.medium && (
|
|
||||||
<DrawerItem name="Medium">
|
|
||||||
{volume.emptyDir.medium}
|
|
||||||
</DrawerItem>
|
|
||||||
)}
|
|
||||||
{ volume.emptyDir.sizeLimit && (
|
|
||||||
<DrawerItem name="Size Limit">
|
|
||||||
{volume.emptyDir.sizeLimit}
|
|
||||||
</DrawerItem>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{claimName && (
|
|
||||||
<DrawerItem name="Claim Name">
|
|
||||||
<Link
|
|
||||||
to={getDetailsUrl(pvcApi.getUrl({
|
|
||||||
name: claimName,
|
|
||||||
namespace: pod.getNs(),
|
|
||||||
}))}
|
|
||||||
>{claimName}
|
|
||||||
</Link>
|
|
||||||
</DrawerItem>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,21 +6,30 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { DrawerItem, DrawerItemProps } from "./drawer-item";
|
import { DrawerItem, DrawerItemProps } from "./drawer-item";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
|
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||||
|
|
||||||
export interface DrawerItemLabelsProps extends DrawerItemProps {
|
export interface DrawerItemLabelsProps extends DrawerItemProps {
|
||||||
labels: string[];
|
labels: string[] | Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DrawerItemLabels(props: DrawerItemLabelsProps) {
|
export function DrawerItemLabels(props: DrawerItemLabelsProps) {
|
||||||
const { labels, ...itemProps } = props;
|
const { labels, ...itemProps } = props;
|
||||||
|
|
||||||
if (!labels || !labels.length) {
|
if (!labels || typeof labels !== "object") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const labelStrings = Array.isArray(labels)
|
||||||
|
? labels
|
||||||
|
: KubeObject.stringifyLabels(labels);
|
||||||
|
|
||||||
|
if (labelStrings.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerItem {...itemProps} labelsOnly>
|
<DrawerItem {...itemProps} labelsOnly>
|
||||||
{labels.map(label => <Badge key={label} label={label} title={label}/>)}
|
{labelStrings.map(label => <Badge key={label} label={label} title={label}/>)}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/renderer/components/drawer/drawer-section.tsx
Normal file
47
src/renderer/components/drawer/drawer-section.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { DrawerTitle, DrawerSubTitle } from "./drawer-title";
|
||||||
|
|
||||||
|
export interface DrawerSectionProps {
|
||||||
|
className?: string;
|
||||||
|
title?: React.ReactNode;
|
||||||
|
hidden?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DrawerSection extends React.Component<DrawerSectionProps> {
|
||||||
|
render() {
|
||||||
|
const { title, children, className, hidden } = this.props;
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DrawerTitle className={className} title={title} />
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DrawerSubSection extends React.Component<DrawerSectionProps> {
|
||||||
|
render() {
|
||||||
|
const { title, children, className, hidden } = this.props;
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DrawerSubTitle className={className} title={title} />
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,3 +8,8 @@
|
|||||||
margin: $margin * 3 (-$margin * 3);
|
margin: $margin * 3 (-$margin * 3);
|
||||||
background: var(--drawerSubtitleBackground);
|
background: var(--drawerSubtitleBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.DrawerSubTitle {
|
||||||
|
padding: $padding $padding * 2;
|
||||||
|
background: var(--drawerSubtitleBackground);
|
||||||
|
}
|
||||||
|
|||||||
@ -23,3 +23,14 @@ export class DrawerTitle extends React.Component<DrawerTitleProps> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class DrawerSubTitle extends React.Component<DrawerTitleProps> {
|
||||||
|
render() {
|
||||||
|
const { title, children, className } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cssNames("DrawerSubTitle", className)}>
|
||||||
|
{title || children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
export * from "./drawer";
|
export * from "./drawer";
|
||||||
export * from "./drawer-title";
|
export * from "./drawer-title";
|
||||||
|
export * from "./drawer-section";
|
||||||
export * from "./drawer-item";
|
export * from "./drawer-item";
|
||||||
export * from "./drawer-item-labels";
|
export * from "./drawer-item-labels";
|
||||||
export * from "./drawer-param-toggler";
|
export * from "./drawer-param-toggler";
|
||||||
|
|||||||
@ -17,8 +17,6 @@ interface TabsContextValue<D = any> {
|
|||||||
onChange?(value: D): void;
|
onChange?(value: D): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
||||||
|
|
||||||
export interface TabsProps<D = any> extends TabsContextValue<D>, Omit<DOMAttributes<HTMLElement>, "onChange"> {
|
export interface TabsProps<D = any> extends TabsContextValue<D>, Omit<DOMAttributes<HTMLElement>, "onChange"> {
|
||||||
className?: string;
|
className?: string;
|
||||||
center?: boolean;
|
center?: boolean;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user