@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,198 @@
|
|
|
1
|
+
// cert-manager CRD utility functions — extracted from resource-utils.ts
|
|
2
|
+
|
|
3
|
+
import type { StatusBadge, HealthLevel } from './resource-utils'
|
|
4
|
+
import { healthColors } from './resource-utils'
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// CERTIFICATE UTILITIES (cert-manager CRD)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export function getCertificateStatus(cert: any): StatusBadge {
|
|
11
|
+
const conditions = cert.status?.conditions || []
|
|
12
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
13
|
+
if (readyCond?.status === 'True') {
|
|
14
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
15
|
+
}
|
|
16
|
+
if (readyCond?.status === 'False') {
|
|
17
|
+
return { text: 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
18
|
+
}
|
|
19
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getCertificateDomains(cert: any): string {
|
|
23
|
+
const dnsNames = cert.spec?.dnsNames || []
|
|
24
|
+
if (dnsNames.length === 0) return '-'
|
|
25
|
+
if (dnsNames.length === 1) return dnsNames[0]
|
|
26
|
+
if (dnsNames.length <= 2) return dnsNames.join(', ')
|
|
27
|
+
return `${dnsNames[0]} +${dnsNames.length - 1}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getCertificateIssuer(cert: any): string {
|
|
31
|
+
const ref = cert.spec?.issuerRef
|
|
32
|
+
if (!ref) return '-'
|
|
33
|
+
return ref.name || '-'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getCertificateExpiry(cert: any): { text: string; level: HealthLevel } {
|
|
37
|
+
const notAfter = cert.status?.notAfter
|
|
38
|
+
if (!notAfter) return { text: '-', level: 'unknown' }
|
|
39
|
+
|
|
40
|
+
const expiryDate = new Date(notAfter)
|
|
41
|
+
const now = new Date()
|
|
42
|
+
const daysUntilExpiry = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
|
|
43
|
+
|
|
44
|
+
if (daysUntilExpiry < 0) {
|
|
45
|
+
return { text: `Expired ${-daysUntilExpiry}d ago`, level: 'unhealthy' }
|
|
46
|
+
}
|
|
47
|
+
if (daysUntilExpiry < 7) {
|
|
48
|
+
return { text: `${daysUntilExpiry}d`, level: 'unhealthy' }
|
|
49
|
+
}
|
|
50
|
+
if (daysUntilExpiry < 30) {
|
|
51
|
+
return { text: `${daysUntilExpiry}d`, level: 'degraded' }
|
|
52
|
+
}
|
|
53
|
+
return { text: `${daysUntilExpiry}d`, level: 'healthy' }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// CERTIFICATE REQUEST UTILITIES (cert-manager)
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
export function getCertificateRequestStatus(cr: any): StatusBadge {
|
|
61
|
+
const conditions = cr.status?.conditions || []
|
|
62
|
+
const denied = conditions.find((c: any) => c.type === 'Denied' && c.status === 'True')
|
|
63
|
+
if (denied) return { text: 'Denied', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
64
|
+
|
|
65
|
+
const ready = conditions.find((c: any) => c.type === 'Ready')
|
|
66
|
+
if (ready?.status === 'True') return { text: 'Issued', color: healthColors.healthy, level: 'healthy' }
|
|
67
|
+
if (ready?.status === 'False') return { text: ready.reason || 'Failed', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
68
|
+
|
|
69
|
+
const approved = conditions.find((c: any) => c.type === 'Approved' && c.status === 'True')
|
|
70
|
+
if (approved) return { text: 'Approved', color: healthColors.degraded, level: 'degraded' }
|
|
71
|
+
|
|
72
|
+
return { text: 'Pending', color: healthColors.degraded, level: 'degraded' }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getCertificateRequestIssuer(cr: any): string {
|
|
76
|
+
const ref = cr.spec?.issuerRef
|
|
77
|
+
if (!ref) return '-'
|
|
78
|
+
return ref.name || '-'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function getCertificateRequestApproved(cr: any): string {
|
|
82
|
+
const conditions = cr.status?.conditions || []
|
|
83
|
+
const approved = conditions.find((c: any) => c.type === 'Approved')
|
|
84
|
+
if (!approved) return 'Pending'
|
|
85
|
+
return approved.status === 'True' ? 'Yes' : 'No'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// CLUSTER ISSUER UTILITIES (cert-manager)
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
export function getClusterIssuerStatus(issuer: any): StatusBadge {
|
|
93
|
+
const conditions = issuer.status?.conditions || []
|
|
94
|
+
const ready = conditions.find((c: any) => c.type === 'Ready')
|
|
95
|
+
if (ready?.status === 'True') return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
96
|
+
if (ready?.status === 'False') return { text: ready.reason || 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
97
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getClusterIssuerType(issuer: any): string {
|
|
101
|
+
const spec = issuer.spec || {}
|
|
102
|
+
if (spec.acme) return 'ACME'
|
|
103
|
+
if (spec.ca) return 'CA'
|
|
104
|
+
if (spec.selfSigned !== undefined) return 'SelfSigned'
|
|
105
|
+
if (spec.vault) return 'Vault'
|
|
106
|
+
if (spec.venafi) return 'Venafi'
|
|
107
|
+
return 'Unknown'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ============================================================================
|
|
111
|
+
// ISSUER UTILITIES (cert-manager)
|
|
112
|
+
// Issuers and ClusterIssuers share the same spec/status schema in cert-manager,
|
|
113
|
+
// differing only in scope (namespaced vs cluster-wide).
|
|
114
|
+
// ============================================================================
|
|
115
|
+
|
|
116
|
+
export const getIssuerStatus = getClusterIssuerStatus
|
|
117
|
+
export const getIssuerType = getClusterIssuerType
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// ORDER UTILITIES (cert-manager ACME)
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
export function getOrderState(order: any): StatusBadge {
|
|
124
|
+
const state = order.status?.state || ''
|
|
125
|
+
switch (state.toLowerCase()) {
|
|
126
|
+
case 'valid':
|
|
127
|
+
return { text: 'Valid', color: healthColors.healthy, level: 'healthy' }
|
|
128
|
+
case 'ready':
|
|
129
|
+
return { text: 'Ready', color: 'bg-blue-500/20 text-blue-400', level: 'healthy' }
|
|
130
|
+
case 'pending':
|
|
131
|
+
return { text: 'Pending', color: healthColors.degraded, level: 'degraded' }
|
|
132
|
+
case 'invalid':
|
|
133
|
+
return { text: 'Invalid', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
134
|
+
case 'expired':
|
|
135
|
+
return { text: 'Expired', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
136
|
+
case 'errored':
|
|
137
|
+
return { text: 'Errored', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
138
|
+
default:
|
|
139
|
+
return { text: state || 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getOrderDomains(order: any): string {
|
|
144
|
+
const dnsNames = order.spec?.dnsNames || []
|
|
145
|
+
if (dnsNames.length === 0) return '-'
|
|
146
|
+
if (dnsNames.length === 1) return dnsNames[0]
|
|
147
|
+
if (dnsNames.length <= 2) return dnsNames.join(', ')
|
|
148
|
+
return `${dnsNames[0]} +${dnsNames.length - 1}`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function getOrderIssuer(order: any): string {
|
|
152
|
+
return order.spec?.issuerRef?.name || '-'
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// CHALLENGE UTILITIES (cert-manager ACME)
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
export function getChallengeState(challenge: any): StatusBadge {
|
|
160
|
+
const state = challenge.status?.state || ''
|
|
161
|
+
switch (state.toLowerCase()) {
|
|
162
|
+
case 'valid':
|
|
163
|
+
return { text: 'Valid', color: healthColors.healthy, level: 'healthy' }
|
|
164
|
+
case 'ready':
|
|
165
|
+
return { text: 'Ready', color: 'bg-blue-500/20 text-blue-400', level: 'healthy' }
|
|
166
|
+
case 'pending':
|
|
167
|
+
return { text: 'Pending', color: healthColors.degraded, level: 'degraded' }
|
|
168
|
+
case 'processing':
|
|
169
|
+
return { text: 'Processing', color: healthColors.degraded, level: 'degraded' }
|
|
170
|
+
case 'invalid':
|
|
171
|
+
return { text: 'Invalid', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
172
|
+
case 'expired':
|
|
173
|
+
return { text: 'Expired', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
174
|
+
case 'errored':
|
|
175
|
+
return { text: 'Errored', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
176
|
+
default:
|
|
177
|
+
return { text: state || 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function getChallengeType(challenge: any): string {
|
|
182
|
+
const type = challenge.spec?.type
|
|
183
|
+
if (type) return type
|
|
184
|
+
if (challenge.spec?.solver?.http01) return 'HTTP-01'
|
|
185
|
+
if (challenge.spec?.solver?.dns01) return 'DNS-01'
|
|
186
|
+
return 'Unknown'
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function getChallengeDomain(challenge: any): string {
|
|
190
|
+
return challenge.spec?.dnsName || '-'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function getChallengePresented(challenge: any): string {
|
|
194
|
+
const presented = challenge.status?.presented
|
|
195
|
+
if (presented === true) return 'Yes'
|
|
196
|
+
if (presented === false) return 'No'
|
|
197
|
+
return '-'
|
|
198
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// CloudNativePG CRD utility functions
|
|
2
|
+
|
|
3
|
+
import type { StatusBadge } from './resource-utils'
|
|
4
|
+
import { healthColors, formatAge, formatDuration } from './resource-utils'
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// CNPG CLUSTER UTILITIES
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export function getCNPGClusterStatus(resource: any): StatusBadge {
|
|
11
|
+
const status = resource.status || {}
|
|
12
|
+
const phase = status.phase || ''
|
|
13
|
+
const instances = resource.spec?.instances ?? 0
|
|
14
|
+
const readyInstances = status.readyInstances ?? 0
|
|
15
|
+
|
|
16
|
+
// Check for degraded instances first
|
|
17
|
+
if (instances > 0 && readyInstances < instances) {
|
|
18
|
+
// Distinguish between total failure and partial degradation
|
|
19
|
+
if (readyInstances === 0) {
|
|
20
|
+
return { text: 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
21
|
+
}
|
|
22
|
+
return { text: 'Degraded', color: healthColors.degraded, level: 'degraded' }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Phase-based status
|
|
26
|
+
const healthyPhases = ['Cluster in healthy state', 'Healthy']
|
|
27
|
+
const transientPhases = [
|
|
28
|
+
'Setting up primary',
|
|
29
|
+
'Creating replica',
|
|
30
|
+
'Switchover in progress',
|
|
31
|
+
'Upgrading cluster',
|
|
32
|
+
'Online upgrade in progress',
|
|
33
|
+
]
|
|
34
|
+
const unhealthyPhases = [
|
|
35
|
+
'Failing over',
|
|
36
|
+
'Failing over (streaming)',
|
|
37
|
+
'Failing over (designated primary)',
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
if (healthyPhases.some(p => phase.includes(p))) {
|
|
41
|
+
return { text: 'Healthy', color: healthColors.healthy, level: 'healthy' }
|
|
42
|
+
}
|
|
43
|
+
if (unhealthyPhases.some(p => phase.includes(p))) {
|
|
44
|
+
return { text: 'Failing Over', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
45
|
+
}
|
|
46
|
+
if (transientPhases.some(p => phase.includes(p))) {
|
|
47
|
+
return { text: phase, color: healthColors.degraded, level: 'degraded' }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Conditions fallback
|
|
51
|
+
const conditions = status.conditions || []
|
|
52
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
53
|
+
if (readyCond?.status === 'True') {
|
|
54
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
55
|
+
}
|
|
56
|
+
if (readyCond?.status === 'False') {
|
|
57
|
+
return { text: readyCond.reason || 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (phase) {
|
|
61
|
+
return { text: phase, color: healthColors.unknown, level: 'unknown' }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getCNPGClusterInstances(resource: any): string {
|
|
68
|
+
const desired = resource.spec?.instances ?? 0
|
|
69
|
+
const ready = resource.status?.readyInstances ?? 0
|
|
70
|
+
return `${ready}/${desired}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getCNPGClusterPrimary(resource: any): string {
|
|
74
|
+
return resource.status?.currentPrimary || resource.status?.targetPrimary || '-'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getCNPGClusterPhase(resource: any): string {
|
|
78
|
+
return resource.status?.phase || '-'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function getCNPGClusterImage(resource: any): string {
|
|
82
|
+
return resource.spec?.imageName || '-'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function getCNPGClusterImageTag(resource: any): string {
|
|
86
|
+
const image = resource.spec?.imageName || ''
|
|
87
|
+
if (!image) return '-'
|
|
88
|
+
const parts = image.split(':')
|
|
89
|
+
if (parts.length > 1) return parts[parts.length - 1]
|
|
90
|
+
return image.split('/').pop() || '-'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getCNPGClusterStorage(resource: any): string {
|
|
94
|
+
return resource.spec?.storage?.size || '-'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getCNPGClusterStorageClass(resource: any): string {
|
|
98
|
+
return resource.spec?.storage?.storageClass || '-'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getCNPGClusterWALStorage(resource: any): { size?: string; storageClass?: string } | null {
|
|
102
|
+
const wal = resource.spec?.walStorage
|
|
103
|
+
if (!wal) return null
|
|
104
|
+
return { size: wal.size, storageClass: wal.storageClass }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function getCNPGClusterBootstrapMethod(resource: any): string {
|
|
108
|
+
const bootstrap = resource.spec?.bootstrap
|
|
109
|
+
if (!bootstrap) return '-'
|
|
110
|
+
if (bootstrap.initdb) return 'initdb'
|
|
111
|
+
if (bootstrap.recovery) return 'recovery'
|
|
112
|
+
if (bootstrap.pg_basebackup) return 'pg_basebackup'
|
|
113
|
+
return '-'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function getCNPGClusterUpdateStrategy(resource: any): string {
|
|
117
|
+
return resource.spec?.primaryUpdateStrategy || 'unsupervised'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function getCNPGClusterBackupConfig(resource: any): {
|
|
121
|
+
configured: boolean
|
|
122
|
+
destinationPath?: string
|
|
123
|
+
retentionPolicy?: string
|
|
124
|
+
lastSuccessfulBackup?: string
|
|
125
|
+
firstRecoverabilityPoint?: string
|
|
126
|
+
} {
|
|
127
|
+
const backup = resource.spec?.backup
|
|
128
|
+
const barman = backup?.barmanObjectStore
|
|
129
|
+
return {
|
|
130
|
+
configured: !!barman,
|
|
131
|
+
destinationPath: barman?.destinationPath,
|
|
132
|
+
retentionPolicy: backup?.retentionPolicy,
|
|
133
|
+
lastSuccessfulBackup: resource.status?.lastSuccessfulBackup,
|
|
134
|
+
firstRecoverabilityPoint: resource.status?.firstRecoverabilityPoint,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function getCNPGClusterMonitoring(resource: any): {
|
|
139
|
+
podMonitorEnabled: boolean
|
|
140
|
+
customQueriesConfigMap?: string[]
|
|
141
|
+
} {
|
|
142
|
+
const monitoring = resource.spec?.monitoring
|
|
143
|
+
return {
|
|
144
|
+
podMonitorEnabled: monitoring?.enablePodMonitor === true,
|
|
145
|
+
customQueriesConfigMap: monitoring?.customQueriesConfigMap?.map((ref: any) =>
|
|
146
|
+
typeof ref === 'string' ? ref : ref.name
|
|
147
|
+
),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function getCNPGClusterIsReplica(resource: any): boolean {
|
|
152
|
+
return !!resource.spec?.replicaCluster
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function getCNPGClusterReplicaSource(resource: any): string {
|
|
156
|
+
const replica = resource.spec?.replicaCluster
|
|
157
|
+
if (!replica) return '-'
|
|
158
|
+
return replica.source || replica.primary || '-'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getCNPGClusterInstanceNames(resource: any): string[] {
|
|
162
|
+
return resource.status?.instanceNames || []
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function getCNPGClusterPostgresParams(resource: any): Record<string, string> {
|
|
166
|
+
return resource.spec?.postgresql?.parameters || {}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// CNPG BACKUP UTILITIES
|
|
171
|
+
// ============================================================================
|
|
172
|
+
|
|
173
|
+
export function getCNPGBackupStatus(resource: any): StatusBadge {
|
|
174
|
+
const phase = resource.status?.phase || ''
|
|
175
|
+
|
|
176
|
+
switch (phase.toLowerCase()) {
|
|
177
|
+
case 'completed':
|
|
178
|
+
return { text: 'Completed', color: healthColors.healthy, level: 'healthy' }
|
|
179
|
+
case 'running':
|
|
180
|
+
return { text: 'Running', color: healthColors.degraded, level: 'degraded' }
|
|
181
|
+
case 'pending':
|
|
182
|
+
return { text: 'Pending', color: healthColors.degraded, level: 'degraded' }
|
|
183
|
+
case 'failed':
|
|
184
|
+
return { text: 'Failed', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
185
|
+
default:
|
|
186
|
+
if (phase) return { text: phase, color: healthColors.unknown, level: 'unknown' }
|
|
187
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function getCNPGBackupCluster(resource: any): string {
|
|
192
|
+
return resource.spec?.cluster?.name || '-'
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function getCNPGBackupMethod(resource: any): string {
|
|
196
|
+
return resource.status?.method || resource.spec?.method || '-'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function getCNPGBackupPhase(resource: any): string {
|
|
200
|
+
return resource.status?.phase || '-'
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function getCNPGBackupDuration(resource: any): string {
|
|
204
|
+
const started = resource.status?.startedAt
|
|
205
|
+
const stopped = resource.status?.stoppedAt
|
|
206
|
+
if (!started) return '-'
|
|
207
|
+
const startTime = new Date(started).getTime()
|
|
208
|
+
const endTime = stopped ? new Date(stopped).getTime() : Date.now()
|
|
209
|
+
return formatDuration(endTime - startTime, true)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function getCNPGBackupStartedAt(resource: any): string {
|
|
213
|
+
return resource.status?.startedAt ? formatAge(resource.status.startedAt) : '-'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function getCNPGBackupStoppedAt(resource: any): string {
|
|
217
|
+
return resource.status?.stoppedAt ? formatAge(resource.status.stoppedAt) : '-'
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function getCNPGBackupName(resource: any): string {
|
|
221
|
+
return resource.status?.backupName || '-'
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function getCNPGBackupDestinationPath(resource: any): string {
|
|
225
|
+
return resource.status?.destinationPath || '-'
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function getCNPGBackupServerName(resource: any): string {
|
|
229
|
+
return resource.status?.serverName || '-'
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function getCNPGBackupError(resource: any): string {
|
|
233
|
+
return resource.status?.error || ''
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function getCNPGBackupTarget(resource: any): string {
|
|
237
|
+
return resource.spec?.target || '-'
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// CNPG SCHEDULED BACKUP UTILITIES
|
|
242
|
+
// ============================================================================
|
|
243
|
+
|
|
244
|
+
export function getCNPGScheduledBackupStatus(resource: any): StatusBadge {
|
|
245
|
+
const isSuspended = resource.spec?.suspend === true
|
|
246
|
+
|
|
247
|
+
if (isSuspended) {
|
|
248
|
+
return { text: 'Suspended', color: healthColors.degraded, level: 'degraded' }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If we have a last schedule time, it's active
|
|
252
|
+
if (resource.status?.lastScheduleTime) {
|
|
253
|
+
return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Check if immediate flag is set and no schedule has run yet
|
|
257
|
+
if (resource.spec?.immediate) {
|
|
258
|
+
return { text: 'Immediate', color: healthColors.healthy, level: 'healthy' }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return { text: 'Scheduled', color: healthColors.healthy, level: 'healthy' }
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function getCNPGScheduledBackupCluster(resource: any): string {
|
|
265
|
+
return resource.spec?.cluster?.name || '-'
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function getCNPGScheduleCron(resource: any): string {
|
|
269
|
+
return resource.spec?.schedule || '-'
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function getCNPGScheduledBackupMethod(resource: any): string {
|
|
273
|
+
return resource.spec?.method || 'barmanObjectStore'
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function getCNPGScheduledBackupLastSchedule(resource: any): string {
|
|
277
|
+
return resource.status?.lastScheduleTime ? formatAge(resource.status.lastScheduleTime) : '-'
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function getCNPGScheduledBackupNextSchedule(resource: any): string {
|
|
281
|
+
const next = resource.status?.nextScheduleTime
|
|
282
|
+
if (!next) return '-'
|
|
283
|
+
const nextDate = new Date(next)
|
|
284
|
+
const now = new Date()
|
|
285
|
+
const diffMs = nextDate.getTime() - now.getTime()
|
|
286
|
+
if (diffMs <= 0) return 'overdue'
|
|
287
|
+
return `in ${formatDuration(diffMs)}`
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function getCNPGScheduledBackupIsSuspended(resource: any): boolean {
|
|
291
|
+
return resource.spec?.suspend === true
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function getCNPGScheduledBackupIsImmediate(resource: any): boolean {
|
|
295
|
+
return resource.spec?.immediate === true
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function getCNPGScheduledBackupOwnerRef(resource: any): string {
|
|
299
|
+
return resource.spec?.backupOwnerReference || 'none'
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============================================================================
|
|
303
|
+
// CNPG POOLER UTILITIES
|
|
304
|
+
// ============================================================================
|
|
305
|
+
|
|
306
|
+
export function getCNPGPoolerStatus(resource: any): StatusBadge {
|
|
307
|
+
const desired = resource.spec?.instances ?? 0
|
|
308
|
+
const ready = resource.status?.instances ?? 0
|
|
309
|
+
|
|
310
|
+
if (desired > 0 && ready < desired) {
|
|
311
|
+
if (ready === 0) {
|
|
312
|
+
return { text: 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
313
|
+
}
|
|
314
|
+
return { text: 'Degraded', color: healthColors.degraded, level: 'degraded' }
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (desired > 0 && ready >= desired) {
|
|
318
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function getCNPGPoolerCluster(resource: any): string {
|
|
325
|
+
return resource.spec?.cluster?.name || '-'
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function getCNPGPoolerType(resource: any): string {
|
|
329
|
+
return resource.spec?.type || '-'
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function getCNPGPoolerMode(resource: any): string {
|
|
333
|
+
return resource.spec?.pgbouncer?.poolMode || 'session'
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function getCNPGPoolerInstances(resource: any): string {
|
|
337
|
+
const desired = resource.spec?.instances ?? 0
|
|
338
|
+
const ready = resource.status?.instances ?? 0
|
|
339
|
+
return `${ready}/${desired}`
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function getCNPGPoolerParameters(resource: any): Record<string, string> {
|
|
343
|
+
return resource.spec?.pgbouncer?.parameters || {}
|
|
344
|
+
}
|