@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,157 @@
|
|
|
1
|
+
import { Server, HardDrive, Shield, Network } from 'lucide-react'
|
|
2
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
|
|
3
|
+
import { getEC2NodeClassStatus } from '../resource-utils-karpenter'
|
|
4
|
+
|
|
5
|
+
interface KarpenterEC2NodeClassRendererProps {
|
|
6
|
+
data: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function KarpenterEC2NodeClassRenderer({ data }: KarpenterEC2NodeClassRendererProps) {
|
|
10
|
+
const status = data.status || {}
|
|
11
|
+
const spec = data.spec || {}
|
|
12
|
+
const conditions = status.conditions || []
|
|
13
|
+
|
|
14
|
+
const nodeClassStatus = getEC2NodeClassStatus(data)
|
|
15
|
+
const isNotReady = nodeClassStatus.level === 'unhealthy'
|
|
16
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
17
|
+
|
|
18
|
+
const amiTerms = spec.amiSelectorTerms || []
|
|
19
|
+
const blockDevices = spec.blockDeviceMappings || []
|
|
20
|
+
const subnetTerms = spec.subnetSelectorTerms || []
|
|
21
|
+
const sgTerms = spec.securityGroupSelectorTerms || []
|
|
22
|
+
const metadataOptions = spec.metadataOptions
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
{isNotReady && (
|
|
27
|
+
<AlertBanner
|
|
28
|
+
variant="error"
|
|
29
|
+
title="EC2NodeClass Not Ready"
|
|
30
|
+
message={readyCond?.message || 'The EC2NodeClass is not in a ready state.'}
|
|
31
|
+
/>
|
|
32
|
+
)}
|
|
33
|
+
|
|
34
|
+
{/* IAM & AMI */}
|
|
35
|
+
<Section title="Instance Configuration" icon={Server}>
|
|
36
|
+
<PropertyList>
|
|
37
|
+
{spec.role && <Property label="IAM Role" value={spec.role} />}
|
|
38
|
+
{amiTerms.length > 0 && (
|
|
39
|
+
<Property
|
|
40
|
+
label="AMI Selector"
|
|
41
|
+
value={amiTerms.map((t: any) => t.alias || t.id || t.name || JSON.stringify(t)).join(', ')}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
{spec.amiFamily && <Property label="AMI Family" value={spec.amiFamily} />}
|
|
45
|
+
</PropertyList>
|
|
46
|
+
</Section>
|
|
47
|
+
|
|
48
|
+
{/* Block Devices */}
|
|
49
|
+
{blockDevices.length > 0 && (
|
|
50
|
+
<Section title={`Block Devices (${blockDevices.length})`} icon={HardDrive}>
|
|
51
|
+
<div className="space-y-2">
|
|
52
|
+
{blockDevices.map((bd: any, i: number) => (
|
|
53
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2">
|
|
54
|
+
<div className="text-sm font-medium text-theme-text-primary mb-1">
|
|
55
|
+
{bd.deviceName || `/dev/xvda`}
|
|
56
|
+
</div>
|
|
57
|
+
<div className="grid grid-cols-2 gap-x-4 gap-y-1 text-xs">
|
|
58
|
+
{bd.ebs?.volumeType && (
|
|
59
|
+
<div>
|
|
60
|
+
<span className="text-theme-text-tertiary">Type: </span>
|
|
61
|
+
<span className="text-theme-text-secondary">{bd.ebs.volumeType}</span>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
{bd.ebs?.volumeSize && (
|
|
65
|
+
<div>
|
|
66
|
+
<span className="text-theme-text-tertiary">Size: </span>
|
|
67
|
+
<span className="text-theme-text-secondary">{bd.ebs.volumeSize}</span>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
{bd.ebs?.iops && (
|
|
71
|
+
<div>
|
|
72
|
+
<span className="text-theme-text-tertiary">IOPS: </span>
|
|
73
|
+
<span className="text-theme-text-secondary">{bd.ebs.iops}</span>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
{bd.ebs?.throughput && (
|
|
77
|
+
<div>
|
|
78
|
+
<span className="text-theme-text-tertiary">Throughput: </span>
|
|
79
|
+
<span className="text-theme-text-secondary">{bd.ebs.throughput}</span>
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
<div>
|
|
83
|
+
<span className="text-theme-text-tertiary">Encrypted: </span>
|
|
84
|
+
<span className="text-theme-text-secondary">{bd.ebs?.encrypted ? 'Yes' : 'No'}</span>
|
|
85
|
+
</div>
|
|
86
|
+
{bd.ebs?.deleteOnTermination !== undefined && (
|
|
87
|
+
<div>
|
|
88
|
+
<span className="text-theme-text-tertiary">Delete on Termination: </span>
|
|
89
|
+
<span className="text-theme-text-secondary">{bd.ebs.deleteOnTermination ? 'Yes' : 'No'}</span>
|
|
90
|
+
</div>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
</Section>
|
|
97
|
+
)}
|
|
98
|
+
|
|
99
|
+
{/* Network - Subnets & Security Groups */}
|
|
100
|
+
<Section title="Network" icon={Network}>
|
|
101
|
+
{subnetTerms.length > 0 && (
|
|
102
|
+
<div className="mb-3">
|
|
103
|
+
<div className="text-xs text-theme-text-tertiary font-medium mb-1">Subnet Selector</div>
|
|
104
|
+
{subnetTerms.map((term: any, i: number) => (
|
|
105
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2 mb-1">
|
|
106
|
+
{term.tags && Object.entries(term.tags).map(([key, val]) => (
|
|
107
|
+
<span key={key} className="inline-flex items-center px-1.5 py-0.5 mr-1 mb-1 bg-theme-hover rounded text-xs text-theme-text-secondary">
|
|
108
|
+
{key}: {String(val)}
|
|
109
|
+
</span>
|
|
110
|
+
))}
|
|
111
|
+
{term.id && (
|
|
112
|
+
<span className="text-xs text-theme-text-secondary">{term.id}</span>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
))}
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
{sgTerms.length > 0 && (
|
|
119
|
+
<div>
|
|
120
|
+
<div className="text-xs text-theme-text-tertiary font-medium mb-1">Security Group Selector</div>
|
|
121
|
+
{sgTerms.map((term: any, i: number) => (
|
|
122
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2 mb-1">
|
|
123
|
+
{term.tags && Object.entries(term.tags).map(([key, val]) => (
|
|
124
|
+
<span key={key} className="inline-flex items-center px-1.5 py-0.5 mr-1 mb-1 bg-theme-hover rounded text-xs text-theme-text-secondary">
|
|
125
|
+
{key}: {String(val)}
|
|
126
|
+
</span>
|
|
127
|
+
))}
|
|
128
|
+
{term.id && (
|
|
129
|
+
<span className="text-xs text-theme-text-secondary">{term.id}</span>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
))}
|
|
133
|
+
</div>
|
|
134
|
+
)}
|
|
135
|
+
</Section>
|
|
136
|
+
|
|
137
|
+
{/* Metadata Options */}
|
|
138
|
+
{metadataOptions && (
|
|
139
|
+
<Section title="Metadata Options" icon={Shield}>
|
|
140
|
+
<PropertyList>
|
|
141
|
+
{metadataOptions.httpTokens && (
|
|
142
|
+
<Property label="HTTP Tokens (IMDSv2)" value={metadataOptions.httpTokens} />
|
|
143
|
+
)}
|
|
144
|
+
{metadataOptions.httpPutResponseHopLimit !== undefined && (
|
|
145
|
+
<Property label="Hop Limit" value={String(metadataOptions.httpPutResponseHopLimit)} />
|
|
146
|
+
)}
|
|
147
|
+
{metadataOptions.httpEndpoint && (
|
|
148
|
+
<Property label="HTTP Endpoint" value={metadataOptions.httpEndpoint} />
|
|
149
|
+
)}
|
|
150
|
+
</PropertyList>
|
|
151
|
+
</Section>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
<ConditionsSection conditions={conditions} />
|
|
155
|
+
</>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Server, Cpu, Settings } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
+
import {
|
|
5
|
+
getNodeClaimStatus,
|
|
6
|
+
getNodeClaimInstanceType,
|
|
7
|
+
getNodeClaimNodeName,
|
|
8
|
+
getNodeClaimCapacity,
|
|
9
|
+
getNodeClaimNodePoolRef,
|
|
10
|
+
getNodeClaimRequirements,
|
|
11
|
+
getNodeClaimNodeClassRef,
|
|
12
|
+
getNodeClaimExpireAfter,
|
|
13
|
+
} from '../resource-utils-karpenter'
|
|
14
|
+
|
|
15
|
+
function formatRelativeTime(isoString: string): string {
|
|
16
|
+
const date = new Date(isoString)
|
|
17
|
+
if (isNaN(date.getTime())) return ''
|
|
18
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000)
|
|
19
|
+
if (seconds < 60) return `${seconds}s ago`
|
|
20
|
+
const minutes = Math.floor(seconds / 60)
|
|
21
|
+
if (minutes < 60) return `${minutes}m ago`
|
|
22
|
+
const hours = Math.floor(minutes / 60)
|
|
23
|
+
if (hours < 24) return `${hours}h ago`
|
|
24
|
+
const days = Math.floor(hours / 24)
|
|
25
|
+
return `${days}d ago`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface KarpenterNodeClaimRendererProps {
|
|
29
|
+
data: any
|
|
30
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string; group?: string }) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function KarpenterNodeClaimRenderer({ data, onNavigate }: KarpenterNodeClaimRendererProps) {
|
|
34
|
+
const status = data.status || {}
|
|
35
|
+
const conditions = status.conditions || []
|
|
36
|
+
|
|
37
|
+
const claimStatus = getNodeClaimStatus(data)
|
|
38
|
+
const isNotReady = claimStatus.level === 'unhealthy'
|
|
39
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
40
|
+
const capacity = getNodeClaimCapacity(data)
|
|
41
|
+
const requirements = getNodeClaimRequirements(data)
|
|
42
|
+
const nodeClassRef = getNodeClaimNodeClassRef(data)
|
|
43
|
+
const expireAfter = getNodeClaimExpireAfter(data)
|
|
44
|
+
|
|
45
|
+
// Provisioning steps for timeline
|
|
46
|
+
const steps = [
|
|
47
|
+
{ type: 'Initialized', label: 'Initialized' },
|
|
48
|
+
{ type: 'Launched', label: 'Launched' },
|
|
49
|
+
{ type: 'Registered', label: 'Registered' },
|
|
50
|
+
{ type: 'Ready', label: 'Ready' },
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
{/* Problem alert */}
|
|
56
|
+
{isNotReady && (
|
|
57
|
+
<AlertBanner
|
|
58
|
+
variant="error"
|
|
59
|
+
title="NodeClaim Not Ready"
|
|
60
|
+
message={readyCond?.message || 'The NodeClaim is not in a ready state.'}
|
|
61
|
+
/>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
{/* Instance Info */}
|
|
65
|
+
<Section title="Instance" icon={Server}>
|
|
66
|
+
<PropertyList>
|
|
67
|
+
<Property label="Instance Type" value={getNodeClaimInstanceType(data)} />
|
|
68
|
+
<Property label="Node Name" value={getNodeClaimNodeName(data)} />
|
|
69
|
+
<Property label="NodePool" value={getNodeClaimNodePoolRef(data)} />
|
|
70
|
+
{nodeClassRef && (
|
|
71
|
+
<Property
|
|
72
|
+
label="NodeClass"
|
|
73
|
+
value={nodeClassRef.name ? (
|
|
74
|
+
<ResourceLink
|
|
75
|
+
name={nodeClassRef.name}
|
|
76
|
+
kind={(nodeClassRef.kind || 'EC2NodeClass').toLowerCase() + 's'}
|
|
77
|
+
namespace=""
|
|
78
|
+
group={nodeClassRef.group}
|
|
79
|
+
label={`${nodeClassRef.kind || 'EC2NodeClass'}/${nodeClassRef.name}`}
|
|
80
|
+
onNavigate={onNavigate}
|
|
81
|
+
/>
|
|
82
|
+
) : '-'}
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
85
|
+
{status.imageID && <Property label="Image ID" value={status.imageID} />}
|
|
86
|
+
{expireAfter && <Property label="Expire After" value={expireAfter} />}
|
|
87
|
+
</PropertyList>
|
|
88
|
+
</Section>
|
|
89
|
+
|
|
90
|
+
{/* Capacity */}
|
|
91
|
+
{Object.keys(capacity).length > 0 && (
|
|
92
|
+
<Section title="Capacity" icon={Cpu}>
|
|
93
|
+
<PropertyList>
|
|
94
|
+
{capacity.cpu && <Property label="CPU" value={capacity.cpu} />}
|
|
95
|
+
{capacity.memory && <Property label="Memory" value={capacity.memory} />}
|
|
96
|
+
{capacity.pods && <Property label="Pods" value={capacity.pods} />}
|
|
97
|
+
{capacity['ephemeral-storage'] && <Property label="Ephemeral Storage" value={capacity['ephemeral-storage']} />}
|
|
98
|
+
</PropertyList>
|
|
99
|
+
</Section>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{/* Requirements */}
|
|
103
|
+
{requirements.length > 0 && (
|
|
104
|
+
<Section title={`Requirements (${requirements.length})`} icon={Settings}>
|
|
105
|
+
<div className="space-y-1">
|
|
106
|
+
{requirements.map((req: any, i: number) => (
|
|
107
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2">
|
|
108
|
+
<div className="flex items-center gap-2 text-sm">
|
|
109
|
+
<span className="text-theme-text-primary font-medium">{req.key}</span>
|
|
110
|
+
<span className="text-theme-text-tertiary">{req.operator}</span>
|
|
111
|
+
</div>
|
|
112
|
+
{req.values && req.values.length > 0 && (
|
|
113
|
+
<div className="mt-1 flex flex-wrap gap-1">
|
|
114
|
+
{req.values.map((v: string, vi: number) => (
|
|
115
|
+
<span key={vi} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
|
|
116
|
+
{v}
|
|
117
|
+
</span>
|
|
118
|
+
))}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
))}
|
|
123
|
+
</div>
|
|
124
|
+
</Section>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{/* Provisioning Timeline */}
|
|
128
|
+
<Section title="Provisioning" defaultExpanded>
|
|
129
|
+
<div className="space-y-1">
|
|
130
|
+
{steps.map((step, index) => {
|
|
131
|
+
const cond = conditions.find((c: any) => c.type === step.type)
|
|
132
|
+
const isComplete = cond?.status === 'True'
|
|
133
|
+
const isFailed = cond?.status === 'False'
|
|
134
|
+
const isPending = !cond
|
|
135
|
+
const transitionTime = cond?.lastTransitionTime
|
|
136
|
+
|
|
137
|
+
// Find the current step (first non-True)
|
|
138
|
+
const currentStepIndex = steps.findIndex((s) => {
|
|
139
|
+
const c = conditions.find((c: any) => c.type === s.type)
|
|
140
|
+
return c?.status !== 'True'
|
|
141
|
+
})
|
|
142
|
+
const isCurrent = index === currentStepIndex
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
key={step.type}
|
|
147
|
+
className={clsx(
|
|
148
|
+
'flex items-center gap-2 px-2 py-1.5 rounded text-sm',
|
|
149
|
+
isCurrent && 'bg-blue-500/10 border border-blue-500/30',
|
|
150
|
+
isComplete && 'opacity-80',
|
|
151
|
+
isPending && !isCurrent && 'opacity-50'
|
|
152
|
+
)}
|
|
153
|
+
>
|
|
154
|
+
<span
|
|
155
|
+
className={clsx(
|
|
156
|
+
'w-5 h-5 rounded-full flex items-center justify-center text-xs shrink-0',
|
|
157
|
+
isComplete && 'bg-green-500/20 text-green-400',
|
|
158
|
+
isCurrent && 'bg-blue-500/20 text-blue-400',
|
|
159
|
+
isFailed && 'bg-red-500/20 text-red-400',
|
|
160
|
+
isPending && !isCurrent && 'bg-gray-500/20 text-gray-400'
|
|
161
|
+
)}
|
|
162
|
+
>
|
|
163
|
+
{isComplete ? '\u2713' : isFailed ? '\u2717' : isCurrent ? '\u25CF' : '\u25CB'}
|
|
164
|
+
</span>
|
|
165
|
+
<span className="text-theme-text-tertiary text-xs w-4 shrink-0">{index}</span>
|
|
166
|
+
<span
|
|
167
|
+
className={clsx(
|
|
168
|
+
'text-sm flex-1',
|
|
169
|
+
isCurrent ? 'text-theme-text-primary font-medium' : 'text-theme-text-secondary'
|
|
170
|
+
)}
|
|
171
|
+
>
|
|
172
|
+
{step.label}
|
|
173
|
+
</span>
|
|
174
|
+
{transitionTime && (
|
|
175
|
+
<span className="text-xs text-theme-text-tertiary shrink-0">
|
|
176
|
+
{formatRelativeTime(transitionTime)}
|
|
177
|
+
</span>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
)
|
|
181
|
+
})}
|
|
182
|
+
</div>
|
|
183
|
+
</Section>
|
|
184
|
+
|
|
185
|
+
<ConditionsSection conditions={conditions} />
|
|
186
|
+
</>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Server, Settings, Shield, Cpu, Tag } from 'lucide-react'
|
|
2
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
3
|
+
import {
|
|
4
|
+
getNodePoolStatus,
|
|
5
|
+
getNodePoolNodeClassRef,
|
|
6
|
+
getNodePoolDisruptionPolicy,
|
|
7
|
+
getNodePoolRequirements,
|
|
8
|
+
getNodePoolWeight,
|
|
9
|
+
} from '../resource-utils-karpenter'
|
|
10
|
+
|
|
11
|
+
interface KarpenterNodePoolRendererProps {
|
|
12
|
+
data: any
|
|
13
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string; group?: string }) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function KarpenterNodePoolRenderer({ data, onNavigate }: KarpenterNodePoolRendererProps) {
|
|
17
|
+
const status = data.status || {}
|
|
18
|
+
const spec = data.spec || {}
|
|
19
|
+
const conditions = status.conditions || []
|
|
20
|
+
|
|
21
|
+
const poolStatus = getNodePoolStatus(data)
|
|
22
|
+
const isNotReady = poolStatus.level === 'unhealthy'
|
|
23
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
24
|
+
const requirements = getNodePoolRequirements(data)
|
|
25
|
+
const weight = getNodePoolWeight(data)
|
|
26
|
+
const disruption = spec.disruption || {}
|
|
27
|
+
const templateLabels = spec.template?.metadata?.labels || {}
|
|
28
|
+
const templateExpireAfter = spec.template?.spec?.expireAfter
|
|
29
|
+
const nodeClassRef = spec.template?.spec?.nodeClassRef
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
{/* Problem alert */}
|
|
34
|
+
{isNotReady && (
|
|
35
|
+
<AlertBanner
|
|
36
|
+
variant="error"
|
|
37
|
+
title="NodePool Not Ready"
|
|
38
|
+
message={readyCond?.message || 'The NodePool is not in a ready state.'}
|
|
39
|
+
/>
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
{/* NodeClass Reference */}
|
|
43
|
+
<Section title="Node Class" icon={Server}>
|
|
44
|
+
<PropertyList>
|
|
45
|
+
<Property
|
|
46
|
+
label="Reference"
|
|
47
|
+
value={nodeClassRef?.name ? (
|
|
48
|
+
<ResourceLink
|
|
49
|
+
name={nodeClassRef.name}
|
|
50
|
+
kind={(nodeClassRef.kind || 'EC2NodeClass').toLowerCase() + 's'}
|
|
51
|
+
namespace=""
|
|
52
|
+
group={nodeClassRef.group}
|
|
53
|
+
label={getNodePoolNodeClassRef(data)}
|
|
54
|
+
onNavigate={onNavigate}
|
|
55
|
+
/>
|
|
56
|
+
) : getNodePoolNodeClassRef(data)}
|
|
57
|
+
/>
|
|
58
|
+
{nodeClassRef?.group && (
|
|
59
|
+
<Property label="API Group" value={nodeClassRef.group} />
|
|
60
|
+
)}
|
|
61
|
+
{nodeClassRef?.kind && (
|
|
62
|
+
<Property label="Kind" value={nodeClassRef.kind} />
|
|
63
|
+
)}
|
|
64
|
+
</PropertyList>
|
|
65
|
+
</Section>
|
|
66
|
+
|
|
67
|
+
{/* Limits */}
|
|
68
|
+
<Section title="Limits" icon={Cpu}>
|
|
69
|
+
<PropertyList>
|
|
70
|
+
{spec.limits?.cpu && <Property label="CPU" value={spec.limits.cpu} />}
|
|
71
|
+
{spec.limits?.memory && <Property label="Memory" value={spec.limits.memory} />}
|
|
72
|
+
{!spec.limits?.cpu && !spec.limits?.memory && (
|
|
73
|
+
<Property label="Limits" value="No limits configured" />
|
|
74
|
+
)}
|
|
75
|
+
{weight !== undefined && <Property label="Weight" value={String(weight)} />}
|
|
76
|
+
</PropertyList>
|
|
77
|
+
</Section>
|
|
78
|
+
|
|
79
|
+
{/* Disruption */}
|
|
80
|
+
<Section title="Disruption" icon={Shield}>
|
|
81
|
+
<PropertyList>
|
|
82
|
+
<Property label="Consolidation Policy" value={getNodePoolDisruptionPolicy(data)} />
|
|
83
|
+
{disruption.consolidateAfter && (
|
|
84
|
+
<Property label="Consolidate After" value={disruption.consolidateAfter} />
|
|
85
|
+
)}
|
|
86
|
+
{(disruption.expireAfter || templateExpireAfter) && (
|
|
87
|
+
<Property label="Expire After" value={disruption.expireAfter || templateExpireAfter} />
|
|
88
|
+
)}
|
|
89
|
+
</PropertyList>
|
|
90
|
+
{disruption.budgets && disruption.budgets.length > 0 && (
|
|
91
|
+
<div className="mt-2 space-y-1">
|
|
92
|
+
<div className="text-xs text-theme-text-tertiary font-medium mb-1">Budgets</div>
|
|
93
|
+
{disruption.budgets.map((budget: any, i: number) => (
|
|
94
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2 text-sm text-theme-text-secondary">
|
|
95
|
+
{budget.nodes && <span>Nodes: {budget.nodes}</span>}
|
|
96
|
+
{budget.schedule && <span className="ml-2">Schedule: {budget.schedule}</span>}
|
|
97
|
+
{budget.duration && <span className="ml-2">Duration: {budget.duration}</span>}
|
|
98
|
+
</div>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
</Section>
|
|
103
|
+
|
|
104
|
+
{/* Template Labels */}
|
|
105
|
+
{Object.keys(templateLabels).length > 0 && (
|
|
106
|
+
<Section title="Template Labels" icon={Tag}>
|
|
107
|
+
<div className="flex flex-wrap gap-1">
|
|
108
|
+
{Object.entries(templateLabels).map(([key, val]) => (
|
|
109
|
+
<span
|
|
110
|
+
key={key}
|
|
111
|
+
className="inline-flex items-center px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary"
|
|
112
|
+
>
|
|
113
|
+
{key}: {String(val)}
|
|
114
|
+
</span>
|
|
115
|
+
))}
|
|
116
|
+
</div>
|
|
117
|
+
</Section>
|
|
118
|
+
)}
|
|
119
|
+
|
|
120
|
+
{/* Requirements */}
|
|
121
|
+
{requirements.length > 0 && (
|
|
122
|
+
<Section title={`Requirements (${requirements.length})`} icon={Settings} defaultExpanded>
|
|
123
|
+
<div className="space-y-1">
|
|
124
|
+
{requirements.map((req: any, i: number) => (
|
|
125
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2">
|
|
126
|
+
<div className="flex items-center gap-2 text-sm">
|
|
127
|
+
<span className="text-theme-text-primary font-medium">{req.key}</span>
|
|
128
|
+
<span className="text-theme-text-tertiary">{req.operator}</span>
|
|
129
|
+
</div>
|
|
130
|
+
{req.values && req.values.length > 0 && (
|
|
131
|
+
<div className="mt-1 flex flex-wrap gap-1">
|
|
132
|
+
{req.values.map((v: string, vi: number) => (
|
|
133
|
+
<span key={vi} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
|
|
134
|
+
{v}
|
|
135
|
+
</span>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
))}
|
|
141
|
+
</div>
|
|
142
|
+
</Section>
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
<ConditionsSection conditions={conditions} />
|
|
146
|
+
</>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Cpu } from 'lucide-react'
|
|
2
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
|
|
3
|
+
import {
|
|
4
|
+
getScaledJobStatus,
|
|
5
|
+
getScaledJobTarget,
|
|
6
|
+
getScaledJobStrategy,
|
|
7
|
+
getScaledJobTriggers,
|
|
8
|
+
} from '../resource-utils-keda'
|
|
9
|
+
|
|
10
|
+
interface KedaScaledJobRendererProps {
|
|
11
|
+
data: any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function KedaScaledJobRenderer({ data }: KedaScaledJobRendererProps) {
|
|
15
|
+
const status = data.status || {}
|
|
16
|
+
const spec = data.spec || {}
|
|
17
|
+
const conditions = status.conditions || []
|
|
18
|
+
|
|
19
|
+
const jobStatus = getScaledJobStatus(data)
|
|
20
|
+
const isNotReady = jobStatus.level === 'unhealthy'
|
|
21
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
22
|
+
const triggers = getScaledJobTriggers(data)
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
{/* Problem alert */}
|
|
27
|
+
{isNotReady && (
|
|
28
|
+
<AlertBanner
|
|
29
|
+
variant="error"
|
|
30
|
+
title="ScaledJob Not Ready"
|
|
31
|
+
message={readyCond?.message || 'The ScaledJob is not in a ready state.'}
|
|
32
|
+
/>
|
|
33
|
+
)}
|
|
34
|
+
|
|
35
|
+
{/* Job Target section */}
|
|
36
|
+
<Section title="Job Scaling" icon={Cpu}>
|
|
37
|
+
<PropertyList>
|
|
38
|
+
<Property label="Job Target" value={getScaledJobTarget(data)} />
|
|
39
|
+
<Property label="Strategy" value={getScaledJobStrategy(data)} />
|
|
40
|
+
{spec.pollingInterval !== undefined && (
|
|
41
|
+
<Property label="Polling Interval" value={`${spec.pollingInterval}s`} />
|
|
42
|
+
)}
|
|
43
|
+
{spec.successfulJobsHistoryLimit !== undefined && (
|
|
44
|
+
<Property label="Success History" value={String(spec.successfulJobsHistoryLimit)} />
|
|
45
|
+
)}
|
|
46
|
+
{spec.failedJobsHistoryLimit !== undefined && (
|
|
47
|
+
<Property label="Failure History" value={String(spec.failedJobsHistoryLimit)} />
|
|
48
|
+
)}
|
|
49
|
+
{spec.maxReplicaCount !== undefined && (
|
|
50
|
+
<Property label="Max Replicas" value={String(spec.maxReplicaCount)} />
|
|
51
|
+
)}
|
|
52
|
+
</PropertyList>
|
|
53
|
+
</Section>
|
|
54
|
+
|
|
55
|
+
{/* Triggers section */}
|
|
56
|
+
{triggers.length > 0 && (
|
|
57
|
+
<Section title={`Triggers (${triggers.length})`} defaultExpanded>
|
|
58
|
+
<div className="space-y-2">
|
|
59
|
+
{triggers.map((trigger, i) => (
|
|
60
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2">
|
|
61
|
+
<div className="flex items-center gap-2 text-sm">
|
|
62
|
+
<span className="text-theme-text-primary font-medium">{trigger.type}</span>
|
|
63
|
+
{trigger.name && (
|
|
64
|
+
<span className="text-theme-text-tertiary">({trigger.name})</span>
|
|
65
|
+
)}
|
|
66
|
+
{trigger.authenticationRef && (
|
|
67
|
+
<span className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
|
|
68
|
+
auth: {trigger.authenticationRef.name}
|
|
69
|
+
</span>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
{trigger.metadata && Object.keys(trigger.metadata).length > 0 && (
|
|
73
|
+
<div className="mt-1 flex flex-wrap gap-1">
|
|
74
|
+
{Object.entries(trigger.metadata).map(([k, v]) => (
|
|
75
|
+
<span key={k} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
|
|
76
|
+
{k}: {v}
|
|
77
|
+
</span>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
</Section>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
<ConditionsSection conditions={conditions} />
|
|
88
|
+
</>
|
|
89
|
+
)
|
|
90
|
+
}
|