@skyhook-io/k8s-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +63 -0
- package/src/components/dock/BottomDock.tsx +204 -0
- package/src/components/dock/DockContext.tsx +212 -0
- package/src/components/dock/TerminalTab.tsx +333 -0
- package/src/components/dock/index.ts +3 -0
- package/src/components/gitops/GitOpsActions.tsx +227 -0
- package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
- package/src/components/gitops/ManagedResourcesList.tsx +210 -0
- package/src/components/gitops/SyncCountdown.tsx +146 -0
- package/src/components/gitops/index.ts +4 -0
- package/src/components/logs/JsonLogLine.tsx +90 -0
- package/src/components/logs/LogCore.tsx +553 -0
- package/src/components/logs/LogToolbarSelects.tsx +74 -0
- package/src/components/logs/LogsViewer.tsx +146 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
- package/src/components/logs/index.ts +13 -0
- package/src/components/logs/useLogBuffer.ts +138 -0
- package/src/components/logs/useLogSearch.ts +151 -0
- package/src/components/logs/useLogStream.ts +61 -0
- package/src/components/resources/ResourcesView.tsx +5494 -0
- package/src/components/resources/index.ts +17 -0
- package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
- package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
- package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
- package/src/components/resources/renderers/EventRenderer.tsx +175 -0
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
- package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
- package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
- package/src/components/resources/renderers/HPARenderer.tsx +164 -0
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
- package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
- package/src/components/resources/renderers/JobRenderer.tsx +103 -0
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
- package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
- package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
- package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
- package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
- package/src/components/resources/renderers/PodRenderer.tsx +677 -0
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
- package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
- package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
- package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
- package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
- package/src/components/resources/renderers/VPARenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
- package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
- package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
- package/src/components/resources/renderers/argo-cells.tsx +118 -0
- package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
- package/src/components/resources/renderers/eso-cells.tsx +115 -0
- package/src/components/resources/renderers/flux-cells.tsx +253 -0
- package/src/components/resources/renderers/index.ts +105 -0
- package/src/components/resources/renderers/istio-cells.tsx +210 -0
- package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
- package/src/components/resources/renderers/keda-cells.tsx +94 -0
- package/src/components/resources/renderers/knative-cells.tsx +396 -0
- package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
- package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
- package/src/components/resources/renderers/traefik-cells.tsx +120 -0
- package/src/components/resources/renderers/trivy-cells.tsx +153 -0
- package/src/components/resources/renderers/trivy-shared.tsx +67 -0
- package/src/components/resources/renderers/velero-cells.tsx +164 -0
- package/src/components/resources/resource-utils-argo.ts +155 -0
- package/src/components/resources/resource-utils-certmanager.ts +198 -0
- package/src/components/resources/resource-utils-cnpg.ts +344 -0
- package/src/components/resources/resource-utils-eso.ts +385 -0
- package/src/components/resources/resource-utils-flux.ts +186 -0
- package/src/components/resources/resource-utils-istio.ts +372 -0
- package/src/components/resources/resource-utils-karpenter.ts +150 -0
- package/src/components/resources/resource-utils-keda.ts +203 -0
- package/src/components/resources/resource-utils-knative.ts +218 -0
- package/src/components/resources/resource-utils-kyverno.ts +224 -0
- package/src/components/resources/resource-utils-prometheus.ts +159 -0
- package/src/components/resources/resource-utils-traefik.ts +180 -0
- package/src/components/resources/resource-utils-trivy.ts +146 -0
- package/src/components/resources/resource-utils-velero.ts +293 -0
- package/src/components/resources/resource-utils.ts +1558 -0
- package/src/components/shared/EditableYamlView.tsx +302 -0
- package/src/components/shared/ResourceActionsBar.tsx +924 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
- package/src/components/shared/index.ts +3 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/timeline/shared.tsx +614 -0
- package/src/components/topology/GroupNode.tsx +198 -0
- package/src/components/topology/K8sResourceNode.tsx +440 -0
- package/src/components/topology/TopologyControls.tsx +62 -0
- package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
- package/src/components/topology/TopologyGraph.tsx +748 -0
- package/src/components/topology/TopologySearch.tsx +304 -0
- package/src/components/topology/index.ts +7 -0
- package/src/components/topology/layout.ts +734 -0
- package/src/components/topology/layout.worker.ts +272 -0
- package/src/components/topology/topology.css +129 -0
- package/src/components/ui/CodeViewer.tsx +451 -0
- package/src/components/ui/ConfirmDialog.tsx +188 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
- package/src/components/ui/HealthRing.tsx +72 -0
- package/src/components/ui/MetricsChart.tsx +259 -0
- package/src/components/ui/ResourceBar.tsx +68 -0
- package/src/components/ui/Toast.tsx +225 -0
- package/src/components/ui/Tooltip.tsx +165 -0
- package/src/components/ui/YamlEditor.tsx +280 -0
- package/src/components/ui/drawer-components.tsx +792 -0
- package/src/components/ui/index.ts +10 -0
- package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
- package/src/components/workload/WorkloadView.tsx +1109 -0
- package/src/components/workload/index.ts +2 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useAnimatedUnmount.ts +30 -0
- package/src/hooks/useKeyboardShortcuts.tsx +336 -0
- package/src/hooks/useRefreshAnimation.ts +58 -0
- package/src/index.ts +35 -0
- package/src/types/core.ts +894 -0
- package/src/types/gitops.ts +458 -0
- package/src/types/index.ts +2 -0
- package/src/utils/animation.ts +40 -0
- package/src/utils/api-resources.ts +185 -0
- package/src/utils/badge-colors.ts +399 -0
- package/src/utils/context-name.ts +72 -0
- package/src/utils/download.ts +19 -0
- package/src/utils/format.ts +233 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/log-format.ts +300 -0
- package/src/utils/navigation.ts +119 -0
- package/src/utils/resource-hierarchy.ts +630 -0
- package/src/utils/resource-icons.ts +206 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { Server, HardDrive, Globe, Tag, Activity, ExternalLink } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
|
|
4
|
+
import { MetricsChart } from '../../ui/MetricsChart'
|
|
5
|
+
import { formatMemoryString } from '../../../utils/format'
|
|
6
|
+
import type { MetricsDataPoint } from '../../../types/core'
|
|
7
|
+
|
|
8
|
+
interface NodeRendererProps {
|
|
9
|
+
data: any
|
|
10
|
+
relationships?: { pods?: any[] }
|
|
11
|
+
onViewPods?: () => void
|
|
12
|
+
metrics?: { usage?: { cpu: string; memory: string }; timestamp?: string }
|
|
13
|
+
metricsHistory?: { dataPoints?: MetricsDataPoint[]; collectionError?: string }
|
|
14
|
+
hideMetricsServer?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Helper to handle undefined values
|
|
18
|
+
function formatMemory(value: string | undefined): string {
|
|
19
|
+
if (!value) return '-'
|
|
20
|
+
return formatMemoryString(value)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Format storage values the same way as memory
|
|
24
|
+
function formatStorage(value: string | undefined): string {
|
|
25
|
+
return formatMemory(value)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Extract problems from node status and spec
|
|
29
|
+
function getNodeProblems(data: any): string[] {
|
|
30
|
+
const problems: string[] = []
|
|
31
|
+
const conditions = data.status?.conditions || []
|
|
32
|
+
const spec = data.spec || {}
|
|
33
|
+
|
|
34
|
+
// Check if unschedulable
|
|
35
|
+
if (spec.unschedulable) {
|
|
36
|
+
problems.push('Node is cordoned (unschedulable)')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const cond of conditions) {
|
|
40
|
+
// NotReady is a problem when status is not True
|
|
41
|
+
if (cond.type === 'Ready' && cond.status !== 'True') {
|
|
42
|
+
problems.push(`Node is NotReady${cond.message ? ': ' + cond.message : ''}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// These conditions are problems when True
|
|
46
|
+
if (cond.status === 'True') {
|
|
47
|
+
if (cond.type === 'DiskPressure') {
|
|
48
|
+
problems.push(`Disk pressure${cond.message ? ': ' + cond.message : ''}`)
|
|
49
|
+
}
|
|
50
|
+
if (cond.type === 'MemoryPressure') {
|
|
51
|
+
problems.push(`Memory pressure${cond.message ? ': ' + cond.message : ''}`)
|
|
52
|
+
}
|
|
53
|
+
if (cond.type === 'PIDPressure') {
|
|
54
|
+
problems.push(`PID pressure${cond.message ? ': ' + cond.message : ''}`)
|
|
55
|
+
}
|
|
56
|
+
if (cond.type === 'NetworkUnavailable') {
|
|
57
|
+
problems.push(`Network unavailable${cond.message ? ': ' + cond.message : ''}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return problems
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function NodeRenderer({ data, relationships, onViewPods, metrics, metricsHistory, hideMetricsServer }: NodeRendererProps) {
|
|
66
|
+
const status = data.status || {}
|
|
67
|
+
const spec = data.spec || {}
|
|
68
|
+
const metadata = data.metadata || {}
|
|
69
|
+
const labels = metadata.labels || {}
|
|
70
|
+
const nodeInfo = status.nodeInfo || {}
|
|
71
|
+
const capacity = status.capacity || {}
|
|
72
|
+
const allocatable = status.allocatable || {}
|
|
73
|
+
const addresses = status.addresses || []
|
|
74
|
+
const taints = spec.taints || []
|
|
75
|
+
|
|
76
|
+
// Check for problems
|
|
77
|
+
const problems = getNodeProblems(data)
|
|
78
|
+
const hasProblems = problems.length > 0
|
|
79
|
+
|
|
80
|
+
// Extract platform info from labels
|
|
81
|
+
const instanceType = labels['node.kubernetes.io/instance-type']
|
|
82
|
+
const zone = labels['topology.kubernetes.io/zone']
|
|
83
|
+
const region = labels['topology.kubernetes.io/region']
|
|
84
|
+
const nodePool = labels['cloud.google.com/gke-nodepool'] || labels['eks.amazonaws.com/nodegroup']
|
|
85
|
+
const machineFamily = labels['cloud.google.com/machine-family']
|
|
86
|
+
const hasPlatformInfo = instanceType || zone || region || nodePool || machineFamily
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
{/* Problems alert - shown at top when there are issues */}
|
|
91
|
+
{hasProblems && (
|
|
92
|
+
<AlertBanner variant="error" title="Issues Detected" items={problems} />
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{/* Node Info */}
|
|
96
|
+
<Section title="Node Info" icon={Server}>
|
|
97
|
+
<PropertyList>
|
|
98
|
+
<Property label="OS" value={nodeInfo.osImage} />
|
|
99
|
+
<Property label="Architecture" value={nodeInfo.architecture} />
|
|
100
|
+
<Property label="Kernel" value={nodeInfo.kernelVersion} />
|
|
101
|
+
<Property label="Container Runtime" value={nodeInfo.containerRuntimeVersion} />
|
|
102
|
+
<Property label="Kubelet" value={nodeInfo.kubeletVersion} />
|
|
103
|
+
<Property label="Kube-Proxy" value={nodeInfo.kubeProxyVersion} />
|
|
104
|
+
</PropertyList>
|
|
105
|
+
</Section>
|
|
106
|
+
|
|
107
|
+
{/* Capacity */}
|
|
108
|
+
<Section title="Capacity" icon={HardDrive}>
|
|
109
|
+
<div className="space-y-1">
|
|
110
|
+
<div className="grid grid-cols-4 gap-2 text-xs text-theme-text-tertiary font-medium mb-2">
|
|
111
|
+
<span>Resource</span>
|
|
112
|
+
<span>Capacity</span>
|
|
113
|
+
<span>Allocatable</span>
|
|
114
|
+
<span>In Use</span>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="grid grid-cols-4 gap-2 text-sm">
|
|
117
|
+
<span className="text-theme-text-secondary">CPU</span>
|
|
118
|
+
<span className="text-theme-text-primary">{capacity.cpu || '-'}</span>
|
|
119
|
+
<span className="text-theme-text-primary">{allocatable.cpu || '-'}</span>
|
|
120
|
+
<span className="text-theme-text-primary font-medium">{metrics?.usage?.cpu || '-'}</span>
|
|
121
|
+
</div>
|
|
122
|
+
<div className="grid grid-cols-4 gap-2 text-sm">
|
|
123
|
+
<span className="text-theme-text-secondary">Memory</span>
|
|
124
|
+
<span className="text-theme-text-primary">{formatMemory(capacity.memory)}</span>
|
|
125
|
+
<span className="text-theme-text-primary">{formatMemory(allocatable.memory)}</span>
|
|
126
|
+
<span className="text-theme-text-primary font-medium">{metrics?.usage?.memory ? formatMemory(metrics.usage.memory) : '-'}</span>
|
|
127
|
+
</div>
|
|
128
|
+
<div className="grid grid-cols-4 gap-2 text-sm">
|
|
129
|
+
<span className="text-theme-text-secondary">Pods</span>
|
|
130
|
+
<span className="text-theme-text-primary">{capacity.pods || '-'}</span>
|
|
131
|
+
<span className="text-theme-text-primary">{allocatable.pods || '-'}</span>
|
|
132
|
+
<span className="text-theme-text-primary font-medium">{relationships?.pods?.length ?? '-'}</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div className="grid grid-cols-4 gap-2 text-sm">
|
|
135
|
+
<span className="text-theme-text-secondary">Ephemeral Storage</span>
|
|
136
|
+
<span className="text-theme-text-primary">{formatStorage(capacity['ephemeral-storage'])}</span>
|
|
137
|
+
<span className="text-theme-text-primary">{formatStorage(allocatable['ephemeral-storage'])}</span>
|
|
138
|
+
<span className="text-theme-text-primary">-</span>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
{onViewPods && (
|
|
142
|
+
<div className="mt-3 pt-3 border-t border-theme-border">
|
|
143
|
+
<button
|
|
144
|
+
onClick={onViewPods}
|
|
145
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-blue-400 hover:text-blue-300 bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/30 rounded transition-colors"
|
|
146
|
+
>
|
|
147
|
+
<ExternalLink className="w-3 h-3" />
|
|
148
|
+
View Pods
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</Section>
|
|
153
|
+
|
|
154
|
+
{/* Resource Usage (from metrics-server) — hidden when Prometheus has CPU/memory data */}
|
|
155
|
+
{!hideMetricsServer && (metrics?.usage || metricsHistory?.dataPoints?.length || metricsHistory?.collectionError) && (
|
|
156
|
+
<Section title="Resource Usage" icon={Activity} defaultExpanded>
|
|
157
|
+
{metricsHistory?.collectionError && !metricsHistory?.dataPoints?.length && (
|
|
158
|
+
<div className="mb-3 rounded-lg border border-yellow-500/30 bg-yellow-500/5 px-3 py-2 text-xs text-yellow-400">
|
|
159
|
+
<span className="font-medium">Metrics collection error:</span>{' '}
|
|
160
|
+
<span className="break-all">{metricsHistory.collectionError}</span>
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{metricsHistory?.dataPoints && metricsHistory.dataPoints.length > 0 ? (
|
|
164
|
+
<div className="space-y-4">
|
|
165
|
+
{/* CPU Usage with Chart */}
|
|
166
|
+
<div className="bg-theme-elevated/30 rounded-lg p-3">
|
|
167
|
+
<div className="text-xs text-theme-text-tertiary mb-1 flex items-center justify-between">
|
|
168
|
+
<span>CPU</span>
|
|
169
|
+
<span className="text-theme-text-quaternary">
|
|
170
|
+
{allocatable.cpu || capacity.cpu || '?'} allocatable
|
|
171
|
+
</span>
|
|
172
|
+
</div>
|
|
173
|
+
<MetricsChart
|
|
174
|
+
dataPoints={metricsHistory.dataPoints}
|
|
175
|
+
type="cpu"
|
|
176
|
+
height={60}
|
|
177
|
+
showAxis={true}
|
|
178
|
+
/>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
{/* Memory Usage with Chart */}
|
|
182
|
+
<div className="bg-theme-elevated/30 rounded-lg p-3">
|
|
183
|
+
<div className="text-xs text-theme-text-tertiary mb-1 flex items-center justify-between">
|
|
184
|
+
<span>Memory</span>
|
|
185
|
+
<span className="text-theme-text-quaternary">
|
|
186
|
+
{formatMemory(allocatable.memory) || formatMemory(capacity.memory) || '?'} allocatable
|
|
187
|
+
</span>
|
|
188
|
+
</div>
|
|
189
|
+
<MetricsChart
|
|
190
|
+
dataPoints={metricsHistory.dataPoints}
|
|
191
|
+
type="memory"
|
|
192
|
+
height={60}
|
|
193
|
+
showAxis={true}
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
) : metrics?.usage ? (
|
|
198
|
+
/* Fallback to simple display if no history yet */
|
|
199
|
+
<div className="space-y-3">
|
|
200
|
+
<div className="bg-theme-elevated/30 rounded-lg p-3">
|
|
201
|
+
<div className="flex items-center justify-between mb-2">
|
|
202
|
+
<span className="text-sm font-medium text-theme-text-primary">CPU</span>
|
|
203
|
+
</div>
|
|
204
|
+
<div className="flex items-baseline gap-2">
|
|
205
|
+
<span className="text-lg font-medium text-blue-400">{metrics.usage.cpu}</span>
|
|
206
|
+
<span className="text-sm text-theme-text-tertiary">
|
|
207
|
+
/ {allocatable.cpu || capacity.cpu || '?'} allocatable
|
|
208
|
+
</span>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
<div className="bg-theme-elevated/30 rounded-lg p-3">
|
|
212
|
+
<div className="flex items-center justify-between mb-2">
|
|
213
|
+
<span className="text-sm font-medium text-theme-text-primary">Memory</span>
|
|
214
|
+
</div>
|
|
215
|
+
<div className="flex items-baseline gap-2">
|
|
216
|
+
<span className="text-lg font-medium text-purple-400">{formatMemory(metrics.usage.memory)}</span>
|
|
217
|
+
<span className="text-sm text-theme-text-tertiary">
|
|
218
|
+
/ {formatMemory(allocatable.memory) || formatMemory(capacity.memory) || '?'} allocatable
|
|
219
|
+
</span>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
) : (
|
|
224
|
+
<div className="text-xs text-theme-text-tertiary">Collecting metrics data...</div>
|
|
225
|
+
)}
|
|
226
|
+
{metrics?.timestamp && (
|
|
227
|
+
<div className="mt-2 text-xs text-theme-text-tertiary">
|
|
228
|
+
Last updated: {new Date(metrics.timestamp).toLocaleTimeString()}
|
|
229
|
+
</div>
|
|
230
|
+
)}
|
|
231
|
+
</Section>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
{/* Addresses */}
|
|
235
|
+
{addresses.length > 0 && (
|
|
236
|
+
<Section title="Addresses" icon={Globe}>
|
|
237
|
+
<PropertyList>
|
|
238
|
+
{addresses.map((addr: any) => (
|
|
239
|
+
<Property key={`${addr.type}-${addr.address}`} label={addr.type} value={addr.address} />
|
|
240
|
+
))}
|
|
241
|
+
</PropertyList>
|
|
242
|
+
</Section>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
{/* Platform Info */}
|
|
246
|
+
{hasPlatformInfo && (
|
|
247
|
+
<Section title="Platform" icon={Tag}>
|
|
248
|
+
<PropertyList>
|
|
249
|
+
<Property label="Instance Type" value={instanceType} />
|
|
250
|
+
<Property label="Zone" value={zone} />
|
|
251
|
+
<Property label="Region" value={region} />
|
|
252
|
+
<Property label="Node Pool" value={nodePool} />
|
|
253
|
+
<Property label="Machine Family" value={machineFamily} />
|
|
254
|
+
</PropertyList>
|
|
255
|
+
</Section>
|
|
256
|
+
)}
|
|
257
|
+
|
|
258
|
+
{/* Taints */}
|
|
259
|
+
{taints.length > 0 && (
|
|
260
|
+
<Section title={`Taints (${taints.length})`} defaultExpanded={taints.length <= 5}>
|
|
261
|
+
<div className="space-y-1">
|
|
262
|
+
{taints.map((taint: any, i: number) => (
|
|
263
|
+
<div key={`${taint.key}-${taint.effect}-${i}`} className="text-sm">
|
|
264
|
+
<span className={clsx(
|
|
265
|
+
'px-2 py-0.5 rounded text-xs',
|
|
266
|
+
taint.effect === 'NoSchedule' ? 'bg-yellow-500/20 text-yellow-400' :
|
|
267
|
+
taint.effect === 'NoExecute' ? 'bg-red-500/20 text-red-400' :
|
|
268
|
+
'bg-blue-500/20 text-blue-400'
|
|
269
|
+
)}>
|
|
270
|
+
{taint.key}{taint.value ? `=${taint.value}` : ''}:{taint.effect}
|
|
271
|
+
</span>
|
|
272
|
+
</div>
|
|
273
|
+
))}
|
|
274
|
+
</div>
|
|
275
|
+
</Section>
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
{/* Conditions */}
|
|
279
|
+
<ConditionsSection conditions={status.conditions} />
|
|
280
|
+
</>
|
|
281
|
+
)
|
|
282
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Package, AlertTriangle, CheckCircle2 } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection } from '../../ui/drawer-components'
|
|
4
|
+
import { formatAge } from '../resource-utils'
|
|
5
|
+
import { formatBytes } from '../../../utils/format'
|
|
6
|
+
import { GitOpsStatusBadge, SyncCountdown } from '../../gitops'
|
|
7
|
+
import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
|
|
8
|
+
|
|
9
|
+
interface OCIRepositoryRendererProps {
|
|
10
|
+
data: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function OCIRepositoryRenderer({ data }: OCIRepositoryRendererProps) {
|
|
14
|
+
const status = data.status || {}
|
|
15
|
+
const spec = data.spec || {}
|
|
16
|
+
const conditions = (status.conditions || []) as FluxCondition[]
|
|
17
|
+
const artifact = status.artifact || {}
|
|
18
|
+
|
|
19
|
+
// Convert to unified GitOps status
|
|
20
|
+
const gitOpsStatus = fluxConditionsToGitOpsStatus(conditions, spec.suspend === true)
|
|
21
|
+
|
|
22
|
+
// Problem detection
|
|
23
|
+
const problems: Array<{ color: 'red' | 'yellow'; message: string }> = []
|
|
24
|
+
|
|
25
|
+
if (gitOpsStatus.suspended) {
|
|
26
|
+
problems.push({ color: 'yellow', message: 'OCIRepository is suspended' })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (gitOpsStatus.health === 'Degraded' && gitOpsStatus.message) {
|
|
30
|
+
problems.push({ color: 'red', message: gitOpsStatus.message })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Extract repository info
|
|
34
|
+
const url = spec.url || ''
|
|
35
|
+
const ref = spec.ref || {}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
{/* Problem alerts */}
|
|
40
|
+
{problems.map((problem, i) => (
|
|
41
|
+
<div
|
|
42
|
+
key={i}
|
|
43
|
+
className={clsx(
|
|
44
|
+
'mb-4 p-3 border rounded-lg',
|
|
45
|
+
problem.color === 'red'
|
|
46
|
+
? 'bg-red-500/10 border-red-500/30'
|
|
47
|
+
: 'bg-yellow-500/10 border-yellow-500/30'
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
<div className="flex items-start gap-2">
|
|
51
|
+
<AlertTriangle
|
|
52
|
+
className={clsx(
|
|
53
|
+
'w-4 h-4 mt-0.5 shrink-0',
|
|
54
|
+
problem.color === 'red' ? 'text-red-400' : 'text-yellow-400'
|
|
55
|
+
)}
|
|
56
|
+
/>
|
|
57
|
+
<div className="flex-1 min-w-0">
|
|
58
|
+
<div
|
|
59
|
+
className={clsx(
|
|
60
|
+
'text-sm font-medium',
|
|
61
|
+
problem.color === 'red' ? 'text-red-400' : 'text-yellow-400'
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
{problem.color === 'red' ? 'Issue Detected' : 'Warning'}
|
|
65
|
+
</div>
|
|
66
|
+
<div
|
|
67
|
+
className={clsx(
|
|
68
|
+
'text-xs mt-1',
|
|
69
|
+
problem.color === 'red' ? 'text-red-300/80' : 'text-yellow-300/80'
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
{problem.message}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
))}
|
|
78
|
+
|
|
79
|
+
{/* Status section */}
|
|
80
|
+
<Section title="Status">
|
|
81
|
+
<div className="space-y-3">
|
|
82
|
+
<GitOpsStatusBadge status={gitOpsStatus} showHealth={false} />
|
|
83
|
+
{spec.interval && (
|
|
84
|
+
<SyncCountdown
|
|
85
|
+
interval={spec.interval}
|
|
86
|
+
lastSyncTime={status.lastHandledReconcileAt}
|
|
87
|
+
suspended={gitOpsStatus.suspended}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
</Section>
|
|
92
|
+
|
|
93
|
+
{/* Source section */}
|
|
94
|
+
<Section title="Source" icon={Package}>
|
|
95
|
+
<PropertyList>
|
|
96
|
+
<Property label="URL" value={url} />
|
|
97
|
+
{ref.tag && <Property label="Tag" value={ref.tag} />}
|
|
98
|
+
{ref.semver && <Property label="Semver" value={ref.semver} />}
|
|
99
|
+
{ref.digest && <Property label="Digest" value={ref.digest} />}
|
|
100
|
+
{spec.provider && <Property label="Provider" value={spec.provider} />}
|
|
101
|
+
{spec.secretRef?.name && (
|
|
102
|
+
<Property label="Secret" value={spec.secretRef.name} />
|
|
103
|
+
)}
|
|
104
|
+
{spec.serviceAccountName && (
|
|
105
|
+
<Property label="Service Account" value={spec.serviceAccountName} />
|
|
106
|
+
)}
|
|
107
|
+
{spec.insecure && <Property label="Insecure" value="Yes" />}
|
|
108
|
+
</PropertyList>
|
|
109
|
+
</Section>
|
|
110
|
+
|
|
111
|
+
{/* Artifact section */}
|
|
112
|
+
{artifact.revision && (
|
|
113
|
+
<Section title="Latest Artifact" icon={CheckCircle2}>
|
|
114
|
+
<PropertyList>
|
|
115
|
+
<Property label="Revision" value={artifact.revision} />
|
|
116
|
+
<Property label="Digest" value={artifact.digest} />
|
|
117
|
+
<Property
|
|
118
|
+
label="Last Updated"
|
|
119
|
+
value={artifact.lastUpdateTime ? formatAge(artifact.lastUpdateTime) : '-'}
|
|
120
|
+
/>
|
|
121
|
+
{artifact.size && (
|
|
122
|
+
<Property label="Size" value={formatBytes(artifact.size)} />
|
|
123
|
+
)}
|
|
124
|
+
{artifact.metadata && Object.keys(artifact.metadata).length > 0 && (
|
|
125
|
+
<Property
|
|
126
|
+
label="Metadata"
|
|
127
|
+
value={Object.entries(artifact.metadata).map(([k, v]) => `${k}=${v}`).join(', ')}
|
|
128
|
+
/>
|
|
129
|
+
)}
|
|
130
|
+
</PropertyList>
|
|
131
|
+
</Section>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{/* Additional Info */}
|
|
135
|
+
{status.observedGeneration !== undefined && (
|
|
136
|
+
<Section title="Additional Info" defaultExpanded={false}>
|
|
137
|
+
<PropertyList>
|
|
138
|
+
<Property label="Observed Generation" value={status.observedGeneration} />
|
|
139
|
+
{status.lastHandledReconcileAt && (
|
|
140
|
+
<Property
|
|
141
|
+
label="Last Reconciled"
|
|
142
|
+
value={formatAge(status.lastHandledReconcileAt)}
|
|
143
|
+
/>
|
|
144
|
+
)}
|
|
145
|
+
</PropertyList>
|
|
146
|
+
</Section>
|
|
147
|
+
)}
|
|
148
|
+
|
|
149
|
+
{/* Conditions section */}
|
|
150
|
+
<ConditionsSection conditions={conditions} />
|
|
151
|
+
</>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ShoppingCart, AlertTriangle, Globe, Shield } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection } from '../../ui/drawer-components'
|
|
4
|
+
|
|
5
|
+
function getOrderStateBadge(state: string): { color: string; text: string } {
|
|
6
|
+
switch (state?.toLowerCase()) {
|
|
7
|
+
case 'valid':
|
|
8
|
+
return { text: 'Valid', color: 'bg-green-500/20 text-green-400' }
|
|
9
|
+
case 'ready':
|
|
10
|
+
return { text: 'Ready', color: 'bg-blue-500/20 text-blue-400' }
|
|
11
|
+
case 'pending':
|
|
12
|
+
return { text: 'Pending', color: 'bg-yellow-500/20 text-yellow-400' }
|
|
13
|
+
case 'invalid':
|
|
14
|
+
return { text: 'Invalid', color: 'bg-red-500/20 text-red-400' }
|
|
15
|
+
case 'expired':
|
|
16
|
+
return { text: 'Expired', color: 'bg-red-500/20 text-red-400' }
|
|
17
|
+
case 'errored':
|
|
18
|
+
return { text: 'Errored', color: 'bg-red-500/20 text-red-400' }
|
|
19
|
+
default:
|
|
20
|
+
return { text: state || 'Unknown', color: 'bg-gray-500/20 text-gray-400' }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function OrderRenderer({ data }: { data: any }) {
|
|
25
|
+
const spec = data.spec || {}
|
|
26
|
+
const status = data.status || {}
|
|
27
|
+
const conditions = status.conditions || []
|
|
28
|
+
const state = status.state || ''
|
|
29
|
+
const dnsNames = spec.dnsNames || []
|
|
30
|
+
const issuerRef = spec.issuerRef || {}
|
|
31
|
+
const authorizations = status.authorizations || []
|
|
32
|
+
|
|
33
|
+
const isError = state === 'invalid' || state === 'expired' || state === 'errored'
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
{/* Problem detection alert */}
|
|
38
|
+
{isError && (
|
|
39
|
+
<div className="mb-4 p-3 bg-red-500/10 border border-red-500/30 rounded-lg">
|
|
40
|
+
<div className="flex items-start gap-2">
|
|
41
|
+
<AlertTriangle className="w-4 h-4 text-red-400 mt-0.5 shrink-0" />
|
|
42
|
+
<div className="flex-1 min-w-0">
|
|
43
|
+
<div className="text-sm font-medium text-red-400">Order {state.charAt(0).toUpperCase() + state.slice(1)}</div>
|
|
44
|
+
{status.reason && (
|
|
45
|
+
<div className="text-xs text-red-300/80 mt-1">{status.reason}</div>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
)}
|
|
51
|
+
|
|
52
|
+
{/* Status */}
|
|
53
|
+
<Section title="Status" icon={Shield}>
|
|
54
|
+
<PropertyList>
|
|
55
|
+
<Property
|
|
56
|
+
label="State"
|
|
57
|
+
value={
|
|
58
|
+
<span className={clsx('px-2 py-0.5 rounded text-xs font-medium', getOrderStateBadge(state).color)}>
|
|
59
|
+
{getOrderStateBadge(state).text}
|
|
60
|
+
</span>
|
|
61
|
+
}
|
|
62
|
+
/>
|
|
63
|
+
{status.url && <Property label="ACME URL" value={status.url} />}
|
|
64
|
+
</PropertyList>
|
|
65
|
+
</Section>
|
|
66
|
+
|
|
67
|
+
{/* Domains */}
|
|
68
|
+
{dnsNames.length > 0 && (
|
|
69
|
+
<Section title="Domains" icon={Globe}>
|
|
70
|
+
<div className="flex flex-wrap gap-1.5">
|
|
71
|
+
{dnsNames.map((name: string) => (
|
|
72
|
+
<span key={name} className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary font-medium">
|
|
73
|
+
{name}
|
|
74
|
+
</span>
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
77
|
+
</Section>
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
{/* Issuer */}
|
|
81
|
+
{issuerRef.name && (
|
|
82
|
+
<Section title="Issuer" icon={Shield}>
|
|
83
|
+
<PropertyList>
|
|
84
|
+
<Property label="Name" value={issuerRef.name} />
|
|
85
|
+
<Property label="Kind" value={issuerRef.kind || 'ClusterIssuer'} />
|
|
86
|
+
{issuerRef.group && <Property label="Group" value={issuerRef.group} />}
|
|
87
|
+
</PropertyList>
|
|
88
|
+
</Section>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{/* Authorizations */}
|
|
92
|
+
{authorizations.length > 0 && (
|
|
93
|
+
<Section title={`Authorizations (${authorizations.length})`} icon={ShoppingCart}>
|
|
94
|
+
<div className="space-y-1">
|
|
95
|
+
{authorizations.map((auth: any, i: number) => (
|
|
96
|
+
<div key={i} className="text-xs text-theme-text-secondary bg-theme-elevated/30 rounded p-2">
|
|
97
|
+
{auth.url || `Authorization ${i + 1}`}
|
|
98
|
+
</div>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
</Section>
|
|
102
|
+
)}
|
|
103
|
+
|
|
104
|
+
{/* Conditions */}
|
|
105
|
+
<ConditionsSection conditions={conditions} />
|
|
106
|
+
</>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { HardDrive } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
+
|
|
5
|
+
interface PVCRendererProps {
|
|
6
|
+
data: any
|
|
7
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const accessModeShorthand: Record<string, string> = {
|
|
11
|
+
ReadWriteOnce: 'RWO',
|
|
12
|
+
ReadOnlyMany: 'ROX',
|
|
13
|
+
ReadWriteMany: 'RWX',
|
|
14
|
+
ReadWriteOncePod: 'RWOP',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function formatAccessModes(modes: string[] | undefined): string | undefined {
|
|
18
|
+
if (!modes || modes.length === 0) return undefined
|
|
19
|
+
return modes.map(m => accessModeShorthand[m] || m).join(', ')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function PVCRenderer({ data, onNavigate }: PVCRendererProps) {
|
|
23
|
+
const status = data.status || {}
|
|
24
|
+
const spec = data.spec || {}
|
|
25
|
+
const annotations = data.metadata?.annotations || {}
|
|
26
|
+
const phase = status.phase
|
|
27
|
+
|
|
28
|
+
// Problem detection
|
|
29
|
+
const isLost = phase === 'Lost'
|
|
30
|
+
const isPending = phase === 'Pending'
|
|
31
|
+
const hasProblems = isLost || isPending
|
|
32
|
+
|
|
33
|
+
// Provisioner info from annotations
|
|
34
|
+
const provisioner = annotations['volume.kubernetes.io/storage-provisioner']
|
|
35
|
+
const selectedNode = annotations['volume.kubernetes.io/selected-node']
|
|
36
|
+
const bindCompleted = annotations['pv.kubernetes.io/bind-completed']
|
|
37
|
+
const hasProvisionerInfo = provisioner || selectedNode || bindCompleted
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{/* Problem alerts */}
|
|
42
|
+
{hasProblems && isLost && (
|
|
43
|
+
<AlertBanner
|
|
44
|
+
variant="error"
|
|
45
|
+
title="Issues Detected"
|
|
46
|
+
message="PVC has lost its bound volume"
|
|
47
|
+
/>
|
|
48
|
+
)}
|
|
49
|
+
|
|
50
|
+
{hasProblems && isPending && (
|
|
51
|
+
<AlertBanner
|
|
52
|
+
variant="warning"
|
|
53
|
+
title="Issues Detected"
|
|
54
|
+
message="PVC is waiting to be bound to a volume"
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
<Section title="Status" icon={HardDrive}>
|
|
59
|
+
<PropertyList>
|
|
60
|
+
<Property
|
|
61
|
+
label="Phase"
|
|
62
|
+
value={
|
|
63
|
+
<span className={clsx(
|
|
64
|
+
phase === 'Bound' && 'text-green-400',
|
|
65
|
+
phase === 'Pending' && 'text-yellow-400',
|
|
66
|
+
phase === 'Lost' && 'text-red-400',
|
|
67
|
+
)}>
|
|
68
|
+
{phase}
|
|
69
|
+
</span>
|
|
70
|
+
}
|
|
71
|
+
/>
|
|
72
|
+
<Property label="Capacity" value={status.capacity?.storage} />
|
|
73
|
+
<Property label="Requested" value={spec.resources?.requests?.storage} />
|
|
74
|
+
<Property label="Storage Class" value={
|
|
75
|
+
spec.storageClassName ? <ResourceLink name={spec.storageClassName} kind="storageclasses" namespace="" onNavigate={onNavigate} /> : undefined
|
|
76
|
+
} />
|
|
77
|
+
<Property label="Access Modes" value={formatAccessModes(spec.accessModes)} />
|
|
78
|
+
<Property label="Volume Mode" value={spec.volumeMode} />
|
|
79
|
+
<Property label="Volume Name" value={
|
|
80
|
+
spec.volumeName ? <ResourceLink name={spec.volumeName} kind="persistentvolumes" namespace="" onNavigate={onNavigate} /> : undefined
|
|
81
|
+
} />
|
|
82
|
+
</PropertyList>
|
|
83
|
+
</Section>
|
|
84
|
+
|
|
85
|
+
{hasProvisionerInfo && (
|
|
86
|
+
<Section title="Provisioner Info">
|
|
87
|
+
<PropertyList>
|
|
88
|
+
<Property label="Provisioner" value={provisioner} />
|
|
89
|
+
<Property label="Selected Node" value={selectedNode} />
|
|
90
|
+
<Property label="Bind Completed" value={bindCompleted} />
|
|
91
|
+
</PropertyList>
|
|
92
|
+
</Section>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
<ConditionsSection conditions={status.conditions} />
|
|
96
|
+
</>
|
|
97
|
+
)
|
|
98
|
+
}
|