@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,385 @@
|
|
|
1
|
+
// External Secrets Operator CRD utility functions
|
|
2
|
+
|
|
3
|
+
import type { StatusBadge } from './resource-utils'
|
|
4
|
+
import { healthColors, formatAge } from './resource-utils'
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// PROVIDER DETECTION
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
const PROVIDER_MAP: Record<string, string> = {
|
|
11
|
+
aws: 'AWS Secrets Manager',
|
|
12
|
+
azurekv: 'Azure Key Vault',
|
|
13
|
+
gcpsm: 'GCP Secret Manager',
|
|
14
|
+
vault: 'HashiCorp Vault',
|
|
15
|
+
kubernetes: 'Kubernetes',
|
|
16
|
+
oracle: 'Oracle Vault',
|
|
17
|
+
ibm: 'IBM Secrets Manager',
|
|
18
|
+
doppler: 'Doppler',
|
|
19
|
+
onepassword: '1Password',
|
|
20
|
+
senhasegura: 'senhasegura',
|
|
21
|
+
gitlab: 'GitLab',
|
|
22
|
+
webhook: 'Webhook',
|
|
23
|
+
fake: 'Fake',
|
|
24
|
+
keepersecurity: 'Keeper Security',
|
|
25
|
+
scaleway: 'Scaleway',
|
|
26
|
+
delinea: 'Delinea',
|
|
27
|
+
chef: 'Chef',
|
|
28
|
+
pulumi: 'Pulumi ESC',
|
|
29
|
+
fortanix: 'Fortanix',
|
|
30
|
+
passworddepot: 'Password Depot',
|
|
31
|
+
device42: 'Device42',
|
|
32
|
+
akeyless: 'Akeyless',
|
|
33
|
+
beyondtrust: 'BeyondTrust',
|
|
34
|
+
infisical: 'Infisical',
|
|
35
|
+
passbolt: 'Passbolt',
|
|
36
|
+
bitwarden: 'Bitwarden',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Detect the provider type from a SecretStore/ClusterSecretStore spec.
|
|
41
|
+
* Checks which provider key exists in spec.provider.
|
|
42
|
+
*/
|
|
43
|
+
export function getSecretStoreProviderType(resource: any): string {
|
|
44
|
+
const provider = resource.spec?.provider
|
|
45
|
+
if (!provider) return 'Unknown'
|
|
46
|
+
|
|
47
|
+
for (const [key, label] of Object.entries(PROVIDER_MAP)) {
|
|
48
|
+
if (provider[key]) return label
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Fallback: return the first key found in provider
|
|
52
|
+
const keys = Object.keys(provider).filter(k => k !== 'retrySettings' && k !== 'controller')
|
|
53
|
+
if (keys.length > 0) return keys[0]
|
|
54
|
+
|
|
55
|
+
return 'Unknown'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the short provider key (e.g., 'aws', 'vault') for color mapping.
|
|
60
|
+
*/
|
|
61
|
+
export function getSecretStoreProviderKey(resource: any): string {
|
|
62
|
+
const provider = resource.spec?.provider
|
|
63
|
+
if (!provider) return 'unknown'
|
|
64
|
+
|
|
65
|
+
for (const key of Object.keys(PROVIDER_MAP)) {
|
|
66
|
+
if (provider[key]) return key
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 'unknown'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get provider-specific details from a SecretStore/ClusterSecretStore.
|
|
74
|
+
* Returns key-value pairs suitable for display, never exposing secret values.
|
|
75
|
+
*/
|
|
76
|
+
export function getSecretStoreProviderDetails(resource: any): Array<{ label: string; value: string }> {
|
|
77
|
+
const provider = resource.spec?.provider
|
|
78
|
+
if (!provider) return []
|
|
79
|
+
|
|
80
|
+
const details: Array<{ label: string; value: string }> = []
|
|
81
|
+
|
|
82
|
+
// AWS
|
|
83
|
+
if (provider.aws) {
|
|
84
|
+
const aws = provider.aws
|
|
85
|
+
if (aws.region) details.push({ label: 'Region', value: aws.region })
|
|
86
|
+
if (aws.service) details.push({ label: 'Service', value: aws.service })
|
|
87
|
+
if (aws.role) details.push({ label: 'Role ARN', value: aws.role })
|
|
88
|
+
if (aws.auth?.jwt?.serviceAccountRef?.name) {
|
|
89
|
+
details.push({ label: 'Service Account', value: aws.auth.jwt.serviceAccountRef.name })
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Azure Key Vault
|
|
94
|
+
if (provider.azurekv) {
|
|
95
|
+
const az = provider.azurekv
|
|
96
|
+
if (az.vaultUrl) details.push({ label: 'Vault URL', value: az.vaultUrl })
|
|
97
|
+
if (az.tenantId) details.push({ label: 'Tenant ID', value: az.tenantId })
|
|
98
|
+
if (az.authType) details.push({ label: 'Auth Type', value: az.authType })
|
|
99
|
+
if (az.environmentType) details.push({ label: 'Environment', value: az.environmentType })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// GCP Secret Manager
|
|
103
|
+
if (provider.gcpsm) {
|
|
104
|
+
const gcp = provider.gcpsm
|
|
105
|
+
if (gcp.projectID) details.push({ label: 'Project ID', value: gcp.projectID })
|
|
106
|
+
if (gcp.location) details.push({ label: 'Location', value: gcp.location })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// HashiCorp Vault
|
|
110
|
+
if (provider.vault) {
|
|
111
|
+
const vault = provider.vault
|
|
112
|
+
if (vault.server) details.push({ label: 'Server', value: vault.server })
|
|
113
|
+
if (vault.path) details.push({ label: 'Path', value: vault.path })
|
|
114
|
+
if (vault.version) details.push({ label: 'Version', value: vault.version })
|
|
115
|
+
if (vault.namespace) details.push({ label: 'Namespace', value: vault.namespace })
|
|
116
|
+
// Auth method detection
|
|
117
|
+
const auth = vault.auth
|
|
118
|
+
if (auth) {
|
|
119
|
+
if (auth.kubernetes) details.push({ label: 'Auth Method', value: 'Kubernetes' })
|
|
120
|
+
else if (auth.appRole) details.push({ label: 'Auth Method', value: 'AppRole' })
|
|
121
|
+
else if (auth.tokenSecretRef) details.push({ label: 'Auth Method', value: 'Token' })
|
|
122
|
+
else if (auth.jwt) details.push({ label: 'Auth Method', value: 'JWT' })
|
|
123
|
+
else if (auth.ldap) details.push({ label: 'Auth Method', value: 'LDAP' })
|
|
124
|
+
else if (auth.userPass) details.push({ label: 'Auth Method', value: 'UserPass' })
|
|
125
|
+
else if (auth.cert) details.push({ label: 'Auth Method', value: 'Certificate' })
|
|
126
|
+
else if (auth.iam) details.push({ label: 'Auth Method', value: 'IAM' })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Kubernetes
|
|
131
|
+
if (provider.kubernetes) {
|
|
132
|
+
const k8s = provider.kubernetes
|
|
133
|
+
if (k8s.server?.url) {
|
|
134
|
+
details.push({ label: 'Server', value: k8s.server.url })
|
|
135
|
+
} else {
|
|
136
|
+
details.push({ label: 'Server', value: 'In-cluster' })
|
|
137
|
+
}
|
|
138
|
+
if (k8s.remoteNamespace) details.push({ label: 'Remote Namespace', value: k8s.remoteNamespace })
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Oracle Vault
|
|
142
|
+
if (provider.oracle) {
|
|
143
|
+
const oracle = provider.oracle
|
|
144
|
+
if (oracle.vault) details.push({ label: 'Vault OCID', value: oracle.vault })
|
|
145
|
+
if (oracle.region) details.push({ label: 'Region', value: oracle.region })
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// IBM
|
|
149
|
+
if (provider.ibm) {
|
|
150
|
+
const ibm = provider.ibm
|
|
151
|
+
if (ibm.serviceUrl) details.push({ label: 'Service URL', value: ibm.serviceUrl })
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Doppler
|
|
155
|
+
if (provider.doppler) {
|
|
156
|
+
const doppler = provider.doppler
|
|
157
|
+
if (doppler.project) details.push({ label: 'Project', value: doppler.project })
|
|
158
|
+
if (doppler.config) details.push({ label: 'Config', value: doppler.config })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 1Password
|
|
162
|
+
if (provider.onepassword) {
|
|
163
|
+
const op = provider.onepassword
|
|
164
|
+
if (op.connectHost) details.push({ label: 'Connect Host', value: op.connectHost })
|
|
165
|
+
if (op.vaults) {
|
|
166
|
+
const vaultNames = Object.keys(op.vaults)
|
|
167
|
+
if (vaultNames.length > 0) details.push({ label: 'Vaults', value: vaultNames.join(', ') })
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Akeyless
|
|
172
|
+
if (provider.akeyless) {
|
|
173
|
+
const ak = provider.akeyless
|
|
174
|
+
if (ak.akeylessGWApiURL) details.push({ label: 'Gateway URL', value: ak.akeylessGWApiURL })
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return details
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// EXTERNALSECRET UTILITIES
|
|
182
|
+
// ============================================================================
|
|
183
|
+
|
|
184
|
+
export function getExternalSecretStatus(resource: any): StatusBadge {
|
|
185
|
+
const conditions = resource.status?.conditions || []
|
|
186
|
+
|
|
187
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
188
|
+
if (readyCond?.status === 'True') {
|
|
189
|
+
return { text: 'Synced', color: healthColors.healthy, level: 'healthy' }
|
|
190
|
+
}
|
|
191
|
+
if (readyCond?.status === 'False') {
|
|
192
|
+
return { text: readyCond.reason || 'Not Synced', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function getExternalSecretStore(resource: any): { name: string; kind: string } {
|
|
199
|
+
const ref = resource.spec?.secretStoreRef
|
|
200
|
+
return {
|
|
201
|
+
name: ref?.name || '-',
|
|
202
|
+
kind: ref?.kind || 'SecretStore',
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function getExternalSecretRefreshInterval(resource: any): string {
|
|
207
|
+
return resource.spec?.refreshInterval || '-'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function getExternalSecretSecretCount(resource: any): number {
|
|
211
|
+
const dataCount = (resource.spec?.data || []).length
|
|
212
|
+
const dataFromCount = (resource.spec?.dataFrom || []).length
|
|
213
|
+
return dataCount + dataFromCount
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function getExternalSecretTargetName(resource: any): string {
|
|
217
|
+
return resource.spec?.target?.name || resource.metadata?.name || '-'
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function getExternalSecretTargetCreationPolicy(resource: any): string {
|
|
221
|
+
return resource.spec?.target?.creationPolicy || 'Owner'
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function getExternalSecretTargetDeletionPolicy(resource: any): string {
|
|
225
|
+
return resource.spec?.target?.deletionPolicy || 'Retain'
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function getExternalSecretLastSync(resource: any): string {
|
|
229
|
+
const refreshTime = resource.status?.refreshTime
|
|
230
|
+
if (!refreshTime) return '-'
|
|
231
|
+
return formatAge(refreshTime)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function getExternalSecretDataMappings(resource: any): Array<{
|
|
235
|
+
secretKey: string
|
|
236
|
+
remoteKey: string
|
|
237
|
+
remoteProperty?: string
|
|
238
|
+
remoteVersion?: string
|
|
239
|
+
}> {
|
|
240
|
+
const data = resource.spec?.data || []
|
|
241
|
+
return data.map((d: any) => ({
|
|
242
|
+
secretKey: d.secretKey || '',
|
|
243
|
+
remoteKey: d.remoteRef?.key || '',
|
|
244
|
+
remoteProperty: d.remoteRef?.property,
|
|
245
|
+
remoteVersion: d.remoteRef?.version,
|
|
246
|
+
}))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function getExternalSecretDataFromSources(resource: any): Array<{
|
|
250
|
+
type: string
|
|
251
|
+
details: string
|
|
252
|
+
}> {
|
|
253
|
+
const dataFrom = resource.spec?.dataFrom || []
|
|
254
|
+
return dataFrom.map((df: any) => {
|
|
255
|
+
if (df.extract) {
|
|
256
|
+
return { type: 'extract', details: df.extract.key || '' }
|
|
257
|
+
}
|
|
258
|
+
if (df.find) {
|
|
259
|
+
return { type: 'find', details: df.find.name?.regexp || df.find.tags ? 'by tags' : 'by name' }
|
|
260
|
+
}
|
|
261
|
+
if (df.sourceRef) {
|
|
262
|
+
return { type: 'sourceRef', details: `${df.sourceRef.kind || ''}/${df.sourceRef.name || ''}` }
|
|
263
|
+
}
|
|
264
|
+
return { type: 'unknown', details: '' }
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get the provider type from an ExternalSecret by reading the secretStoreRef
|
|
270
|
+
* and resolving via the store's spec. Since we don't have the store data here,
|
|
271
|
+
* this is a best-effort based on available info.
|
|
272
|
+
*/
|
|
273
|
+
export function getExternalSecretProvider(resource: any): string {
|
|
274
|
+
// ExternalSecrets don't directly contain provider info.
|
|
275
|
+
// The provider is on the SecretStore. Return the store name as a hint.
|
|
276
|
+
const store = getExternalSecretStore(resource)
|
|
277
|
+
return store.name !== '-' ? store.name : '-'
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ============================================================================
|
|
281
|
+
// CLUSTEREXTERNALSECRET UTILITIES
|
|
282
|
+
// ============================================================================
|
|
283
|
+
|
|
284
|
+
export function getClusterExternalSecretStatus(resource: any): StatusBadge {
|
|
285
|
+
const conditions = resource.status?.conditions || []
|
|
286
|
+
const failedNamespaces = resource.status?.failedNamespaces || []
|
|
287
|
+
|
|
288
|
+
if (failedNamespaces.length > 0) {
|
|
289
|
+
return { text: 'Partial Failure', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
293
|
+
if (readyCond?.status === 'True') {
|
|
294
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
295
|
+
}
|
|
296
|
+
if (readyCond?.status === 'False') {
|
|
297
|
+
return { text: readyCond.reason || 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Check for PartiallyReady or similar
|
|
301
|
+
const partialCond = conditions.find((c: any) => c.type === 'PartiallyReady')
|
|
302
|
+
if (partialCond?.status === 'True') {
|
|
303
|
+
return { text: 'Partial', color: healthColors.degraded, level: 'degraded' }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function getClusterExternalSecretNamespaceCount(resource: any): number {
|
|
310
|
+
const provisioned = resource.status?.provisionedNamespaces || []
|
|
311
|
+
return provisioned.length
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function getClusterExternalSecretFailedCount(resource: any): number {
|
|
315
|
+
const failed = resource.status?.failedNamespaces || []
|
|
316
|
+
// failedNamespaces can be an array of strings or objects with namespace+reason
|
|
317
|
+
return failed.length
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function getClusterExternalSecretNamespaces(resource: any): string[] {
|
|
321
|
+
// From spec: either namespaceSelector or explicit namespaces
|
|
322
|
+
return resource.spec?.namespaces || []
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function getClusterExternalSecretNamespaceSelector(resource: any): Record<string, string> | null {
|
|
326
|
+
return resource.spec?.namespaceSelector?.matchLabels || null
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function getClusterExternalSecretProvisionedNamespaces(resource: any): string[] {
|
|
330
|
+
return resource.status?.provisionedNamespaces || []
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function getClusterExternalSecretFailedNamespaces(resource: any): Array<{
|
|
334
|
+
namespace: string
|
|
335
|
+
reason?: string
|
|
336
|
+
}> {
|
|
337
|
+
const failed = resource.status?.failedNamespaces || []
|
|
338
|
+
return failed.map((f: any) => {
|
|
339
|
+
if (typeof f === 'string') return { namespace: f }
|
|
340
|
+
return { namespace: f.namespace || f.name || '', reason: f.reason || f.message }
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ============================================================================
|
|
345
|
+
// SECRETSTORE UTILITIES
|
|
346
|
+
// ============================================================================
|
|
347
|
+
|
|
348
|
+
export function getSecretStoreStatus(resource: any): StatusBadge {
|
|
349
|
+
const conditions = resource.status?.conditions || []
|
|
350
|
+
|
|
351
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
352
|
+
if (readyCond?.status === 'True') {
|
|
353
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
354
|
+
}
|
|
355
|
+
if (readyCond?.status === 'False') {
|
|
356
|
+
return { text: readyCond.reason || 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function getSecretStoreRetrySettings(resource: any): {
|
|
363
|
+
maxRetries?: number
|
|
364
|
+
retryInterval?: string
|
|
365
|
+
} | null {
|
|
366
|
+
const retry = resource.spec?.retrySettings
|
|
367
|
+
if (!retry) return null
|
|
368
|
+
return {
|
|
369
|
+
maxRetries: retry.maxRetries,
|
|
370
|
+
retryInterval: retry.retryInterval,
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function getSecretStoreController(resource: any): string | null {
|
|
375
|
+
return resource.spec?.controller || null
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ============================================================================
|
|
379
|
+
// CLUSTERSECRETSTORE UTILITIES
|
|
380
|
+
// ============================================================================
|
|
381
|
+
|
|
382
|
+
// ClusterSecretStore uses the same structure as SecretStore
|
|
383
|
+
export function getClusterSecretStoreStatus(resource: any): StatusBadge {
|
|
384
|
+
return getSecretStoreStatus(resource)
|
|
385
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// FluxCD CRD utility functions — extracted from resource-utils.ts
|
|
2
|
+
|
|
3
|
+
import type { StatusBadge } from './resource-utils'
|
|
4
|
+
import { healthColors } from './resource-utils'
|
|
5
|
+
import type { FluxCondition } from '../../types/gitops'
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// FLUXCD STATUS UTILITIES
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generic status function for FluxCD resources that follow the standard Ready condition pattern.
|
|
13
|
+
* Works for: GitRepository, OCIRepository, HelmRepository, Alert
|
|
14
|
+
*/
|
|
15
|
+
export function getFluxResourceStatus(resource: any): StatusBadge {
|
|
16
|
+
const conditions: FluxCondition[] = resource.status?.conditions || []
|
|
17
|
+
const readyCondition = conditions.find((c) => c.type === 'Ready')
|
|
18
|
+
|
|
19
|
+
if (resource.spec?.suspend) {
|
|
20
|
+
return { text: 'Suspended', color: healthColors.degraded, level: 'degraded' }
|
|
21
|
+
}
|
|
22
|
+
if (readyCondition?.status === 'True') {
|
|
23
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
24
|
+
}
|
|
25
|
+
if (readyCondition?.status === 'False') {
|
|
26
|
+
return { text: 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
27
|
+
}
|
|
28
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Aliases for specific resource types (all use the same logic)
|
|
32
|
+
export const getGitRepositoryStatus = getFluxResourceStatus
|
|
33
|
+
export const getOCIRepositoryStatus = getFluxResourceStatus
|
|
34
|
+
export const getHelmRepositoryStatus = getFluxResourceStatus
|
|
35
|
+
export const getFluxAlertStatus = getFluxResourceStatus
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Kustomization has additional Healthy condition check
|
|
39
|
+
*/
|
|
40
|
+
export function getKustomizationStatus(ks: any): StatusBadge {
|
|
41
|
+
const conditions: FluxCondition[] = ks.status?.conditions || []
|
|
42
|
+
const readyCondition = conditions.find((c) => c.type === 'Ready')
|
|
43
|
+
const healthyCondition = conditions.find((c) => c.type === 'Healthy')
|
|
44
|
+
|
|
45
|
+
if (ks.spec?.suspend) {
|
|
46
|
+
return { text: 'Suspended', color: healthColors.degraded, level: 'degraded' }
|
|
47
|
+
}
|
|
48
|
+
if (readyCondition?.status === 'True') {
|
|
49
|
+
// Check health condition for more nuanced status
|
|
50
|
+
if (healthyCondition?.status === 'False') {
|
|
51
|
+
return { text: 'Unhealthy', color: healthColors.degraded, level: 'degraded' }
|
|
52
|
+
}
|
|
53
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
54
|
+
}
|
|
55
|
+
if (readyCondition?.status === 'False') {
|
|
56
|
+
return { text: 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
57
|
+
}
|
|
58
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* HelmRelease has Released condition and remediation detection
|
|
63
|
+
*/
|
|
64
|
+
export function getFluxHelmReleaseStatus(hr: any): StatusBadge {
|
|
65
|
+
const conditions: FluxCondition[] = hr.status?.conditions || []
|
|
66
|
+
const readyCondition = conditions.find((c) => c.type === 'Ready')
|
|
67
|
+
const releasedCondition = conditions.find((c) => c.type === 'Released')
|
|
68
|
+
|
|
69
|
+
if (hr.spec?.suspend) {
|
|
70
|
+
return { text: 'Suspended', color: healthColors.degraded, level: 'degraded' }
|
|
71
|
+
}
|
|
72
|
+
if (readyCondition?.status === 'True') {
|
|
73
|
+
return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
|
|
74
|
+
}
|
|
75
|
+
if (readyCondition?.status === 'False') {
|
|
76
|
+
// Check if it's a remediation in progress
|
|
77
|
+
const reason = readyCondition?.reason || ''
|
|
78
|
+
if (reason.includes('Remediation') || reason.includes('Retry')) {
|
|
79
|
+
return { text: 'Remediating', color: healthColors.degraded, level: 'degraded' }
|
|
80
|
+
}
|
|
81
|
+
return { text: 'Failed', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
82
|
+
}
|
|
83
|
+
if (releasedCondition?.status === 'True') {
|
|
84
|
+
return { text: 'Released', color: healthColors.healthy, level: 'healthy' }
|
|
85
|
+
}
|
|
86
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// FLUXCD TABLE CELL UTILITIES
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
export function getGitRepositoryUrl(repo: any): string {
|
|
94
|
+
return repo.spec?.url || '-'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getGitRepositoryRef(repo: any): string {
|
|
98
|
+
const ref = repo.spec?.ref
|
|
99
|
+
if (!ref) return '-'
|
|
100
|
+
if (ref.branch) return ref.branch
|
|
101
|
+
if (ref.tag) return ref.tag
|
|
102
|
+
if (ref.semver) return ref.semver
|
|
103
|
+
if (ref.commit) return ref.commit.substring(0, 7)
|
|
104
|
+
return '-'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function getGitRepositoryRevision(repo: any): string {
|
|
108
|
+
const artifact = repo.status?.artifact
|
|
109
|
+
if (!artifact?.revision) return '-'
|
|
110
|
+
// Format: "branch@sha1:abc123..." or just "sha1:abc123"
|
|
111
|
+
const rev = artifact.revision
|
|
112
|
+
const shaMatch = rev.match(/sha1:([a-f0-9]+)/)
|
|
113
|
+
if (shaMatch) return shaMatch[1].substring(0, 7)
|
|
114
|
+
return rev.substring(0, 12)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function getOCIRepositoryUrl(repo: any): string {
|
|
118
|
+
return repo.spec?.url || '-'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getOCIRepositoryRef(repo: any): string {
|
|
122
|
+
const ref = repo.spec?.ref
|
|
123
|
+
if (!ref) return '-'
|
|
124
|
+
if (ref.tag) return ref.tag
|
|
125
|
+
if (ref.semver) return ref.semver
|
|
126
|
+
if (ref.digest) return ref.digest.substring(0, 12)
|
|
127
|
+
return '-'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getOCIRepositoryRevision(repo: any): string {
|
|
131
|
+
const artifact = repo.status?.artifact
|
|
132
|
+
if (!artifact?.revision) return '-'
|
|
133
|
+
// Usually a digest like "sha256:abc123..."
|
|
134
|
+
const rev = artifact.revision
|
|
135
|
+
if (rev.startsWith('sha256:')) return rev.substring(7, 19)
|
|
136
|
+
return rev.substring(0, 12)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getHelmRepositoryUrl(repo: any): string {
|
|
140
|
+
return repo.spec?.url || '-'
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getHelmRepositoryType(repo: any): string {
|
|
144
|
+
return repo.spec?.type || 'default'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function getKustomizationSource(ks: any): string {
|
|
148
|
+
const ref = ks.spec?.sourceRef
|
|
149
|
+
if (!ref) return '-'
|
|
150
|
+
return `${ref.kind}/${ref.name}`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getKustomizationPath(ks: any): string {
|
|
154
|
+
return ks.spec?.path || './'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function getKustomizationInventory(ks: any): number {
|
|
158
|
+
return ks.status?.inventory?.entries?.length || 0
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getFluxHelmReleaseChart(hr: any): string {
|
|
162
|
+
const chart = hr.spec?.chart?.spec
|
|
163
|
+
if (!chart) return '-'
|
|
164
|
+
return chart.chart || '-'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function getFluxHelmReleaseVersion(hr: any): string {
|
|
168
|
+
const chart = hr.spec?.chart?.spec
|
|
169
|
+
if (!chart?.version) return '*'
|
|
170
|
+
return chart.version
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function getFluxHelmReleaseRevision(hr: any): number {
|
|
174
|
+
// Helm release revision number is in history[0].version
|
|
175
|
+
return hr.status?.history?.[0]?.version || 0
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function getFluxAlertProvider(alert: any): string {
|
|
179
|
+
const ref = alert.spec?.providerRef
|
|
180
|
+
if (!ref) return '-'
|
|
181
|
+
return ref.name || '-'
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function getFluxAlertEventCount(alert: any): number {
|
|
185
|
+
return alert.spec?.eventSources?.length || 0
|
|
186
|
+
}
|