@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,119 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { KeyRound, ChevronDown, ChevronRight } from 'lucide-react'
|
|
3
|
+
import { clsx } from 'clsx'
|
|
4
|
+
import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
5
|
+
import { formatAge } from '../resource-utils'
|
|
6
|
+
import { SEVERITY_BADGE_COLORS, TrivyAlertBanner, formatTrivyImage } from './trivy-shared'
|
|
7
|
+
|
|
8
|
+
interface ExposedSecretReportRendererProps {
|
|
9
|
+
data: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const INITIAL_SHOW_COUNT = 50
|
|
13
|
+
|
|
14
|
+
export function ExposedSecretReportRenderer({ data }: ExposedSecretReportRendererProps) {
|
|
15
|
+
const [showAll, setShowAll] = useState(false)
|
|
16
|
+
const [expanded, setExpanded] = useState(true)
|
|
17
|
+
|
|
18
|
+
const report = data.report || {}
|
|
19
|
+
const summary = report.summary || {}
|
|
20
|
+
const secrets = Array.isArray(report.secrets) ? report.secrets : []
|
|
21
|
+
const scanner = report.scanner || {}
|
|
22
|
+
const labels = data.metadata?.labels || {}
|
|
23
|
+
|
|
24
|
+
const critical = summary.criticalCount || 0
|
|
25
|
+
const high = summary.highCount || 0
|
|
26
|
+
const medium = summary.mediumCount || 0
|
|
27
|
+
const low = summary.lowCount || 0
|
|
28
|
+
|
|
29
|
+
const containerName = labels['trivy-operator.container.name'] || '-'
|
|
30
|
+
const image = formatTrivyImage(report)
|
|
31
|
+
|
|
32
|
+
const displayedSecrets = showAll ? secrets : secrets.slice(0, INITIAL_SHOW_COUNT)
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<TrivyAlertBanner critical={critical} high={high} noun="exposed secret" />
|
|
37
|
+
|
|
38
|
+
{/* Report Overview */}
|
|
39
|
+
<Section title="Report Overview" icon={KeyRound}>
|
|
40
|
+
<PropertyList>
|
|
41
|
+
<Property label="Container" value={containerName} />
|
|
42
|
+
<Property label="Image" value={image} />
|
|
43
|
+
<Property label="Scanner" value={scanner.name ? `${scanner.name} ${scanner.version || ''}`.trim() : '-'} />
|
|
44
|
+
<Property label="Scanned" value={report.updateTimestamp ? formatAge(report.updateTimestamp) + ' ago' : '-'} />
|
|
45
|
+
</PropertyList>
|
|
46
|
+
</Section>
|
|
47
|
+
|
|
48
|
+
{/* Severity Summary */}
|
|
49
|
+
<Section title="Summary">
|
|
50
|
+
<div className="flex flex-wrap gap-2">
|
|
51
|
+
{[
|
|
52
|
+
{ label: 'Critical', count: critical, key: 'CRITICAL' },
|
|
53
|
+
{ label: 'High', count: high, key: 'HIGH' },
|
|
54
|
+
{ label: 'Medium', count: medium, key: 'MEDIUM' },
|
|
55
|
+
{ label: 'Low', count: low, key: 'LOW' },
|
|
56
|
+
].filter(({ count }) => count > 0).map(({ label, count, key }) => (
|
|
57
|
+
<span key={key} className={clsx('px-2 py-0.5 rounded text-xs font-medium', SEVERITY_BADGE_COLORS[key])}>
|
|
58
|
+
{count} {label}
|
|
59
|
+
</span>
|
|
60
|
+
))}
|
|
61
|
+
{secrets.length === 0 && (
|
|
62
|
+
<span className="px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400">No secrets found</span>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</Section>
|
|
66
|
+
|
|
67
|
+
{/* Secrets Table */}
|
|
68
|
+
{secrets.length > 0 && (
|
|
69
|
+
<Section title="Exposed Secrets">
|
|
70
|
+
<button
|
|
71
|
+
onClick={() => setExpanded(!expanded)}
|
|
72
|
+
className="flex items-center gap-1 text-xs text-theme-text-secondary hover:text-theme-text-primary mb-2"
|
|
73
|
+
>
|
|
74
|
+
{expanded ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5" />}
|
|
75
|
+
{secrets.length} secret{secrets.length !== 1 ? 's' : ''}
|
|
76
|
+
</button>
|
|
77
|
+
{expanded && (
|
|
78
|
+
<div className="overflow-x-auto -mx-1">
|
|
79
|
+
<table className="w-full text-xs">
|
|
80
|
+
<thead>
|
|
81
|
+
<tr className="border-b border-theme-border text-theme-text-tertiary">
|
|
82
|
+
<th className="text-left py-1.5 px-1 font-medium">Rule</th>
|
|
83
|
+
<th className="text-left py-1.5 px-1 font-medium">Severity</th>
|
|
84
|
+
<th className="text-left py-1.5 px-1 font-medium">Category</th>
|
|
85
|
+
<th className="text-left py-1.5 px-1 font-medium">Target</th>
|
|
86
|
+
<th className="text-left py-1.5 px-1 font-medium">Match</th>
|
|
87
|
+
</tr>
|
|
88
|
+
</thead>
|
|
89
|
+
<tbody>
|
|
90
|
+
{displayedSecrets.map((secret: any, i: number) => (
|
|
91
|
+
<tr key={`${secret.ruleID || i}-${i}`} className="border-b border-theme-border/50 hover:bg-theme-hover/50">
|
|
92
|
+
<td className="py-1.5 px-1 text-theme-text-secondary font-mono">{secret.ruleID || '-'}</td>
|
|
93
|
+
<td className="py-1.5 px-1">
|
|
94
|
+
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium', SEVERITY_BADGE_COLORS[secret.severity] || 'bg-gray-500/20 text-gray-400')}>
|
|
95
|
+
{secret.severity || '-'}
|
|
96
|
+
</span>
|
|
97
|
+
</td>
|
|
98
|
+
<td className="py-1.5 px-1 text-theme-text-secondary">{secret.category || secret.title || '-'}</td>
|
|
99
|
+
<td className="py-1.5 px-1 text-theme-text-secondary max-w-[180px] truncate font-mono" title={secret.target}>{secret.target || '-'}</td>
|
|
100
|
+
<td className="py-1.5 px-1 text-theme-text-tertiary max-w-[150px] truncate font-mono" title={secret.match}>{secret.match || '-'}</td>
|
|
101
|
+
</tr>
|
|
102
|
+
))}
|
|
103
|
+
</tbody>
|
|
104
|
+
</table>
|
|
105
|
+
{!showAll && secrets.length > INITIAL_SHOW_COUNT && (
|
|
106
|
+
<button
|
|
107
|
+
onClick={() => setShowAll(true)}
|
|
108
|
+
className="mt-2 text-xs text-blue-400 hover:text-blue-300 hover:underline"
|
|
109
|
+
>
|
|
110
|
+
Show all {secrets.length} secrets
|
|
111
|
+
</button>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</Section>
|
|
116
|
+
)}
|
|
117
|
+
</>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { KeyRound, RefreshCw, CloudCog } from 'lucide-react'
|
|
2
|
+
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
3
|
+
import {
|
|
4
|
+
getExternalSecretStatus,
|
|
5
|
+
getExternalSecretStore,
|
|
6
|
+
getExternalSecretRefreshInterval,
|
|
7
|
+
getExternalSecretLastSync,
|
|
8
|
+
getExternalSecretTargetName,
|
|
9
|
+
getExternalSecretTargetCreationPolicy,
|
|
10
|
+
getExternalSecretTargetDeletionPolicy,
|
|
11
|
+
getExternalSecretDataMappings,
|
|
12
|
+
getExternalSecretDataFromSources,
|
|
13
|
+
} from '../resource-utils-eso'
|
|
14
|
+
|
|
15
|
+
interface ExternalSecretRendererProps {
|
|
16
|
+
data: any
|
|
17
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ExternalSecretRenderer({ data, onNavigate }: ExternalSecretRendererProps) {
|
|
21
|
+
const status = data.status || {}
|
|
22
|
+
const conditions = status.conditions || []
|
|
23
|
+
|
|
24
|
+
const esStatus = getExternalSecretStatus(data)
|
|
25
|
+
const store = getExternalSecretStore(data)
|
|
26
|
+
const dataMappings = getExternalSecretDataMappings(data)
|
|
27
|
+
const dataFromSources = getExternalSecretDataFromSources(data)
|
|
28
|
+
const targetTemplate = data.spec?.target?.template
|
|
29
|
+
|
|
30
|
+
// Problem detection
|
|
31
|
+
const readyCond = conditions.find((c: any) => c.type === 'Ready')
|
|
32
|
+
const isNotReady = readyCond?.status === 'False'
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
{/* Alert if not synced */}
|
|
37
|
+
{isNotReady && (
|
|
38
|
+
<AlertBanner
|
|
39
|
+
variant="error"
|
|
40
|
+
title="ExternalSecret Not Synced"
|
|
41
|
+
message={readyCond?.message || 'The ExternalSecret is not in a synced state.'}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{/* Status section */}
|
|
46
|
+
<Section title="Sync Status" icon={RefreshCw} defaultExpanded>
|
|
47
|
+
<PropertyList>
|
|
48
|
+
<Property label="Status" value={esStatus.text} />
|
|
49
|
+
<Property label="Last Sync" value={getExternalSecretLastSync(data)} />
|
|
50
|
+
<Property label="Refresh Interval" value={getExternalSecretRefreshInterval(data)} />
|
|
51
|
+
<Property label="Target Secret" value={(() => {
|
|
52
|
+
const targetName = getExternalSecretTargetName(data)
|
|
53
|
+
if (targetName && targetName !== '-') {
|
|
54
|
+
return (
|
|
55
|
+
<ResourceLink
|
|
56
|
+
name={targetName}
|
|
57
|
+
kind="secrets"
|
|
58
|
+
namespace={data.metadata?.namespace || ''}
|
|
59
|
+
onNavigate={onNavigate}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
return targetName
|
|
64
|
+
})()} />
|
|
65
|
+
{status.syncedResourceVersion && (
|
|
66
|
+
<Property label="Synced Version" value={status.syncedResourceVersion} />
|
|
67
|
+
)}
|
|
68
|
+
{status.binding?.name && (
|
|
69
|
+
<Property label="Binding" value={status.binding.name} />
|
|
70
|
+
)}
|
|
71
|
+
</PropertyList>
|
|
72
|
+
</Section>
|
|
73
|
+
|
|
74
|
+
{/* Store Reference */}
|
|
75
|
+
<Section title="Store Reference" icon={CloudCog} defaultExpanded>
|
|
76
|
+
<PropertyList>
|
|
77
|
+
<Property label="Store Name" value={(() => {
|
|
78
|
+
if (store.name && store.name !== '-') {
|
|
79
|
+
const storeKind = store.kind === 'ClusterSecretStore'
|
|
80
|
+
? 'clustersecretstores'
|
|
81
|
+
: 'secretstores'
|
|
82
|
+
return (
|
|
83
|
+
<ResourceLink
|
|
84
|
+
name={store.name}
|
|
85
|
+
kind={storeKind}
|
|
86
|
+
namespace={store.kind === 'ClusterSecretStore' ? '' : (data.metadata?.namespace || '')}
|
|
87
|
+
onNavigate={onNavigate}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
return store.name
|
|
92
|
+
})()} />
|
|
93
|
+
<Property label="Store Kind" value={store.kind} />
|
|
94
|
+
</PropertyList>
|
|
95
|
+
</Section>
|
|
96
|
+
|
|
97
|
+
{/* Secret Mappings - data[] */}
|
|
98
|
+
{dataMappings.length > 0 && (
|
|
99
|
+
<Section title={`Secret Mappings (${dataMappings.length})`} icon={KeyRound} defaultExpanded>
|
|
100
|
+
<div className="overflow-x-auto">
|
|
101
|
+
<table className="w-full text-sm">
|
|
102
|
+
<thead>
|
|
103
|
+
<tr className="text-theme-text-secondary text-left text-xs uppercase tracking-wider">
|
|
104
|
+
<th className="pb-2 pr-3 font-medium">Secret Key</th>
|
|
105
|
+
<th className="pb-2 pr-3 font-medium">Remote Key</th>
|
|
106
|
+
<th className="pb-2 font-medium">Property</th>
|
|
107
|
+
</tr>
|
|
108
|
+
</thead>
|
|
109
|
+
<tbody>
|
|
110
|
+
{dataMappings.map((mapping, i) => (
|
|
111
|
+
<tr key={i} className="border-t border-theme-border/50">
|
|
112
|
+
<td className="py-1.5 pr-3 text-theme-text-primary font-mono text-xs">{mapping.secretKey || '-'}</td>
|
|
113
|
+
<td className="py-1.5 pr-3 text-theme-text-secondary font-mono text-xs break-all">{mapping.remoteKey || '-'}</td>
|
|
114
|
+
<td className="py-1.5 text-theme-text-secondary text-xs">
|
|
115
|
+
{mapping.remoteProperty || '-'}
|
|
116
|
+
{mapping.remoteVersion && (
|
|
117
|
+
<span className="ml-1 text-theme-text-tertiary">v{mapping.remoteVersion}</span>
|
|
118
|
+
)}
|
|
119
|
+
</td>
|
|
120
|
+
</tr>
|
|
121
|
+
))}
|
|
122
|
+
</tbody>
|
|
123
|
+
</table>
|
|
124
|
+
</div>
|
|
125
|
+
</Section>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{/* DataFrom sources */}
|
|
129
|
+
{dataFromSources.length > 0 && (
|
|
130
|
+
<Section title={`Data Sources (${dataFromSources.length})`} defaultExpanded>
|
|
131
|
+
<div className="space-y-1">
|
|
132
|
+
{dataFromSources.map((source, i) => (
|
|
133
|
+
<div key={i} className="bg-theme-elevated/30 rounded p-2">
|
|
134
|
+
<div className="flex items-center gap-2 text-sm">
|
|
135
|
+
<span className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary font-medium">
|
|
136
|
+
{source.type}
|
|
137
|
+
</span>
|
|
138
|
+
{source.details && (
|
|
139
|
+
<span className="text-theme-text-tertiary text-xs font-mono break-all">{source.details}</span>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
</Section>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{/* Target Configuration */}
|
|
149
|
+
<Section title="Target Configuration" defaultExpanded={false}>
|
|
150
|
+
<PropertyList>
|
|
151
|
+
<Property label="Target Name" value={getExternalSecretTargetName(data)} />
|
|
152
|
+
<Property label="Creation Policy" value={getExternalSecretTargetCreationPolicy(data)} />
|
|
153
|
+
<Property label="Deletion Policy" value={getExternalSecretTargetDeletionPolicy(data)} />
|
|
154
|
+
{targetTemplate?.type && (
|
|
155
|
+
<Property label="Template Type" value={targetTemplate.type} />
|
|
156
|
+
)}
|
|
157
|
+
{targetTemplate?.engineVersion && (
|
|
158
|
+
<Property label="Engine Version" value={targetTemplate.engineVersion} />
|
|
159
|
+
)}
|
|
160
|
+
{targetTemplate?.metadata?.labels && (
|
|
161
|
+
<Property label="Template Labels" value={Object.entries(targetTemplate.metadata.labels).map(([k, v]) => `${k}=${v}`).join(', ')} />
|
|
162
|
+
)}
|
|
163
|
+
{targetTemplate?.metadata?.annotations && (
|
|
164
|
+
<Property label="Template Annotations" value={Object.entries(targetTemplate.metadata.annotations).map(([k, v]) => `${k}=${v}`).join(', ')} />
|
|
165
|
+
)}
|
|
166
|
+
</PropertyList>
|
|
167
|
+
</Section>
|
|
168
|
+
|
|
169
|
+
<ConditionsSection conditions={conditions} />
|
|
170
|
+
</>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { Package, Settings, CheckCircle2, History } from 'lucide-react'
|
|
2
|
+
import { clsx } from 'clsx'
|
|
3
|
+
import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts } from '../../ui/drawer-components'
|
|
4
|
+
import { formatAge } from '../resource-utils'
|
|
5
|
+
import { GitOpsStatusBadge, SyncCountdown } from '../../gitops'
|
|
6
|
+
import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
|
|
7
|
+
|
|
8
|
+
interface FluxHelmReleaseRendererProps {
|
|
9
|
+
data: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function FluxHelmReleaseRenderer({ data }: FluxHelmReleaseRendererProps) {
|
|
13
|
+
const status = data.status || {}
|
|
14
|
+
const spec = data.spec || {}
|
|
15
|
+
const conditions = (status.conditions || []) as FluxCondition[]
|
|
16
|
+
const history = status.history || []
|
|
17
|
+
|
|
18
|
+
// Convert to unified GitOps status
|
|
19
|
+
const gitOpsStatus = fluxConditionsToGitOpsStatus(conditions, spec.suspend === true)
|
|
20
|
+
|
|
21
|
+
// Problem detection
|
|
22
|
+
const problems: Array<{ color: 'red' | 'yellow'; message: string }> = []
|
|
23
|
+
|
|
24
|
+
if (gitOpsStatus.suspended) {
|
|
25
|
+
problems.push({ color: 'yellow', message: 'HelmRelease is suspended' })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (gitOpsStatus.health === 'Degraded' && gitOpsStatus.message) {
|
|
29
|
+
problems.push({ color: 'red', message: gitOpsStatus.message })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check for test failures
|
|
33
|
+
const testFailCondition = conditions.find(
|
|
34
|
+
(c) => c.type === 'TestSuccess' && c.status === 'False'
|
|
35
|
+
)
|
|
36
|
+
if (testFailCondition) {
|
|
37
|
+
problems.push({
|
|
38
|
+
color: 'yellow',
|
|
39
|
+
message: testFailCondition.message || 'Helm tests failed',
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Chart reference
|
|
44
|
+
const chartRef = spec.chart?.spec || {}
|
|
45
|
+
const sourceRef = chartRef.sourceRef || {}
|
|
46
|
+
|
|
47
|
+
// Current release info
|
|
48
|
+
const lastRelease = history.length > 0 ? history[0] : null
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<ProblemAlerts problems={problems} />
|
|
53
|
+
|
|
54
|
+
{/* Status section */}
|
|
55
|
+
<Section title="Status">
|
|
56
|
+
<div className="space-y-3">
|
|
57
|
+
<GitOpsStatusBadge status={gitOpsStatus} />
|
|
58
|
+
{spec.interval && (
|
|
59
|
+
<SyncCountdown
|
|
60
|
+
interval={spec.interval}
|
|
61
|
+
lastSyncTime={status.lastHandledReconcileAt}
|
|
62
|
+
suspended={gitOpsStatus.suspended}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
</Section>
|
|
67
|
+
|
|
68
|
+
{/* Chart section */}
|
|
69
|
+
<Section title="Chart" icon={Package}>
|
|
70
|
+
<PropertyList>
|
|
71
|
+
<Property label="Chart" value={chartRef.chart} />
|
|
72
|
+
<Property label="Version" value={chartRef.version || '*'} />
|
|
73
|
+
<Property label="Source Kind" value={sourceRef.kind} />
|
|
74
|
+
<Property label="Source Name" value={sourceRef.name} />
|
|
75
|
+
{sourceRef.namespace && (
|
|
76
|
+
<Property label="Source Namespace" value={sourceRef.namespace} />
|
|
77
|
+
)}
|
|
78
|
+
<Property
|
|
79
|
+
label="Reconcile Strategy"
|
|
80
|
+
value={chartRef.reconcileStrategy || 'ChartVersion'}
|
|
81
|
+
/>
|
|
82
|
+
</PropertyList>
|
|
83
|
+
</Section>
|
|
84
|
+
|
|
85
|
+
{/* Release Configuration */}
|
|
86
|
+
<Section title="Configuration" icon={Settings}>
|
|
87
|
+
<PropertyList>
|
|
88
|
+
<Property label="Release Name" value={spec.releaseName || data.metadata?.name} />
|
|
89
|
+
<Property label="Target Namespace" value={spec.targetNamespace || data.metadata?.namespace} />
|
|
90
|
+
{spec.timeout && <Property label="Timeout" value={spec.timeout} />}
|
|
91
|
+
{spec.serviceAccountName && (
|
|
92
|
+
<Property label="Service Account" value={spec.serviceAccountName} />
|
|
93
|
+
)}
|
|
94
|
+
{spec.maxHistory !== undefined && (
|
|
95
|
+
<Property label="Max History" value={spec.maxHistory} />
|
|
96
|
+
)}
|
|
97
|
+
</PropertyList>
|
|
98
|
+
</Section>
|
|
99
|
+
|
|
100
|
+
{/* Install/Upgrade settings */}
|
|
101
|
+
{(spec.install || spec.upgrade) && (
|
|
102
|
+
<Section title="Install/Upgrade Settings" defaultExpanded={false}>
|
|
103
|
+
<PropertyList>
|
|
104
|
+
{spec.install && (
|
|
105
|
+
<>
|
|
106
|
+
<Property
|
|
107
|
+
label="Create Namespace"
|
|
108
|
+
value={spec.install.createNamespace ? 'Yes' : 'No'}
|
|
109
|
+
/>
|
|
110
|
+
<Property
|
|
111
|
+
label="Replace CRDs"
|
|
112
|
+
value={spec.install.crds || 'Skip'}
|
|
113
|
+
/>
|
|
114
|
+
{spec.install.remediation && (
|
|
115
|
+
<Property
|
|
116
|
+
label="Install Retries"
|
|
117
|
+
value={spec.install.remediation.retries}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
</>
|
|
121
|
+
)}
|
|
122
|
+
{spec.upgrade && (
|
|
123
|
+
<>
|
|
124
|
+
<Property
|
|
125
|
+
label="Cleanup on Fail"
|
|
126
|
+
value={spec.upgrade.cleanupOnFail ? 'Yes' : 'No'}
|
|
127
|
+
/>
|
|
128
|
+
<Property
|
|
129
|
+
label="Force Upgrade"
|
|
130
|
+
value={spec.upgrade.force ? 'Yes' : 'No'}
|
|
131
|
+
/>
|
|
132
|
+
{spec.upgrade.remediation && (
|
|
133
|
+
<>
|
|
134
|
+
<Property
|
|
135
|
+
label="Upgrade Retries"
|
|
136
|
+
value={spec.upgrade.remediation.retries}
|
|
137
|
+
/>
|
|
138
|
+
<Property
|
|
139
|
+
label="Rollback on Fail"
|
|
140
|
+
value={spec.upgrade.remediation.remediateLastFailure ? 'Yes' : 'No'}
|
|
141
|
+
/>
|
|
142
|
+
</>
|
|
143
|
+
)}
|
|
144
|
+
</>
|
|
145
|
+
)}
|
|
146
|
+
</PropertyList>
|
|
147
|
+
</Section>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
{/* Current Release */}
|
|
151
|
+
{lastRelease && (
|
|
152
|
+
<Section title="Current Release" icon={CheckCircle2}>
|
|
153
|
+
<PropertyList>
|
|
154
|
+
<Property label="Revision" value={lastRelease.revision} />
|
|
155
|
+
<Property label="Chart Version" value={lastRelease.chartVersion} />
|
|
156
|
+
<Property label="App Version" value={lastRelease.appVersion} />
|
|
157
|
+
<Property label="Status" value={lastRelease.status} />
|
|
158
|
+
{lastRelease.firstDeployed && (
|
|
159
|
+
<Property
|
|
160
|
+
label="First Deployed"
|
|
161
|
+
value={formatAge(lastRelease.firstDeployed)}
|
|
162
|
+
/>
|
|
163
|
+
)}
|
|
164
|
+
{lastRelease.lastDeployed && (
|
|
165
|
+
<Property
|
|
166
|
+
label="Last Deployed"
|
|
167
|
+
value={formatAge(lastRelease.lastDeployed)}
|
|
168
|
+
/>
|
|
169
|
+
)}
|
|
170
|
+
</PropertyList>
|
|
171
|
+
</Section>
|
|
172
|
+
)}
|
|
173
|
+
|
|
174
|
+
{/* Release History */}
|
|
175
|
+
{history.length > 1 && (
|
|
176
|
+
<Section title={`History (${history.length} revisions)`} icon={History} defaultExpanded={false}>
|
|
177
|
+
<div className="space-y-2 max-h-48 overflow-y-auto">
|
|
178
|
+
{history.map((release: any, idx: number) => (
|
|
179
|
+
<div
|
|
180
|
+
key={idx}
|
|
181
|
+
className={clsx(
|
|
182
|
+
'p-2 rounded text-sm',
|
|
183
|
+
idx === 0
|
|
184
|
+
? 'bg-green-500/10 border border-green-500/30'
|
|
185
|
+
: 'bg-theme-elevated/30'
|
|
186
|
+
)}
|
|
187
|
+
>
|
|
188
|
+
<div className="flex items-center justify-between">
|
|
189
|
+
<span className="font-medium text-theme-text-primary">
|
|
190
|
+
Rev {release.revision}
|
|
191
|
+
</span>
|
|
192
|
+
<span
|
|
193
|
+
className={clsx(
|
|
194
|
+
'px-1.5 py-0.5 rounded text-xs',
|
|
195
|
+
release.status === 'deployed'
|
|
196
|
+
? 'status-healthy'
|
|
197
|
+
: release.status === 'failed'
|
|
198
|
+
? 'status-unhealthy'
|
|
199
|
+
: 'status-neutral'
|
|
200
|
+
)}
|
|
201
|
+
>
|
|
202
|
+
{release.status}
|
|
203
|
+
</span>
|
|
204
|
+
</div>
|
|
205
|
+
<div className="text-xs text-theme-text-secondary mt-1">
|
|
206
|
+
{release.chartVersion}
|
|
207
|
+
{release.appVersion && ` (app: ${release.appVersion})`}
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
))}
|
|
211
|
+
</div>
|
|
212
|
+
</Section>
|
|
213
|
+
)}
|
|
214
|
+
|
|
215
|
+
{/* Revision info */}
|
|
216
|
+
{(status.helmChart || status.lastAppliedRevision || status.lastAttemptedRevision) && (
|
|
217
|
+
<Section title="Revision Info" defaultExpanded={false}>
|
|
218
|
+
<PropertyList>
|
|
219
|
+
{status.helmChart && (
|
|
220
|
+
<Property label="Helm Chart" value={status.helmChart} />
|
|
221
|
+
)}
|
|
222
|
+
{status.lastAppliedRevision && (
|
|
223
|
+
<Property label="Applied Revision" value={status.lastAppliedRevision} />
|
|
224
|
+
)}
|
|
225
|
+
{status.lastAttemptedRevision && (
|
|
226
|
+
<Property label="Attempted Revision" value={status.lastAttemptedRevision} />
|
|
227
|
+
)}
|
|
228
|
+
{status.lastHandledReconcileAt && (
|
|
229
|
+
<Property
|
|
230
|
+
label="Last Reconciled"
|
|
231
|
+
value={formatAge(status.lastHandledReconcileAt)}
|
|
232
|
+
/>
|
|
233
|
+
)}
|
|
234
|
+
</PropertyList>
|
|
235
|
+
</Section>
|
|
236
|
+
)}
|
|
237
|
+
|
|
238
|
+
{/* Conditions section */}
|
|
239
|
+
<ConditionsSection conditions={conditions} />
|
|
240
|
+
</>
|
|
241
|
+
)
|
|
242
|
+
}
|