@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,159 @@
|
|
|
1
|
+
// Prometheus Operator CRD utility functions
|
|
2
|
+
|
|
3
|
+
import type { StatusBadge } from './resource-utils'
|
|
4
|
+
import { healthColors } from './resource-utils'
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// SHARED HELPERS
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
function getConditionStatus(resource: any): StatusBadge {
|
|
11
|
+
const conditions = resource.status?.conditions || []
|
|
12
|
+
|
|
13
|
+
const reconciledCond = conditions.find((c: any) => c.type === 'Reconciled')
|
|
14
|
+
if (reconciledCond?.status === 'True') {
|
|
15
|
+
return { text: 'Reconciled', color: healthColors.healthy, level: 'healthy' }
|
|
16
|
+
}
|
|
17
|
+
if (reconciledCond?.status === 'False') {
|
|
18
|
+
return { text: reconciledCond.reason || 'Not Reconciled', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const availableCond = conditions.find((c: any) => c.type === 'Available')
|
|
22
|
+
if (availableCond?.status === 'True') {
|
|
23
|
+
return { text: 'Available', color: healthColors.healthy, level: 'healthy' }
|
|
24
|
+
}
|
|
25
|
+
if (availableCond?.status === 'False') {
|
|
26
|
+
return { text: availableCond.reason || 'Unavailable', color: healthColors.unhealthy, level: 'unhealthy' }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fallback: if resource exists but has no conditions, it's likely active
|
|
30
|
+
if (resource.spec) {
|
|
31
|
+
return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatMatchLabels(selector: any): string {
|
|
38
|
+
const matchLabels = selector?.matchLabels
|
|
39
|
+
if (!matchLabels || Object.keys(matchLabels).length === 0) return '-'
|
|
40
|
+
return Object.entries(matchLabels).map(([k, v]) => `${k}=${v}`).join(', ')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// SERVICEMONITOR UTILITIES
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
export function getServiceMonitorStatus(resource: any): StatusBadge {
|
|
48
|
+
return getConditionStatus(resource)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getServiceMonitorEndpointCount(resource: any): number {
|
|
52
|
+
return (resource.spec?.endpoints || []).length
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getServiceMonitorJobLabel(resource: any): string {
|
|
56
|
+
return resource.spec?.jobLabel || '-'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getServiceMonitorSelector(resource: any): string {
|
|
60
|
+
return formatMatchLabels(resource.spec?.selector)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getServiceMonitorEndpoints(resource: any): Array<{
|
|
64
|
+
port?: string
|
|
65
|
+
path?: string
|
|
66
|
+
interval?: string
|
|
67
|
+
scheme?: string
|
|
68
|
+
}> {
|
|
69
|
+
return (resource.spec?.endpoints || []).map((ep: any) => ({
|
|
70
|
+
port: ep.port || ep.targetPort,
|
|
71
|
+
path: ep.path || '/metrics',
|
|
72
|
+
interval: ep.interval,
|
|
73
|
+
scheme: ep.scheme,
|
|
74
|
+
}))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getServiceMonitorNamespaceSelector(resource: any): string {
|
|
78
|
+
const ns = resource.spec?.namespaceSelector
|
|
79
|
+
if (!ns) return 'Same namespace'
|
|
80
|
+
if (ns.any) return 'All namespaces'
|
|
81
|
+
if (ns.matchNames?.length) return ns.matchNames.join(', ')
|
|
82
|
+
return 'Same namespace'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// PROMETHEUSRULE UTILITIES
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
export function getPrometheusRuleStatus(resource: any): StatusBadge {
|
|
90
|
+
return getConditionStatus(resource)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getPrometheusRuleGroupCount(resource: any): number {
|
|
94
|
+
return (resource.spec?.groups || []).length
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getPrometheusRuleTotalRules(resource: any): number {
|
|
98
|
+
const groups = resource.spec?.groups || []
|
|
99
|
+
return groups.reduce((sum: number, g: any) => sum + (g.rules?.length || 0), 0)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getPrometheusRuleGroups(resource: any): Array<{
|
|
103
|
+
name: string
|
|
104
|
+
interval?: string
|
|
105
|
+
ruleCount: number
|
|
106
|
+
alertCount: number
|
|
107
|
+
recordCount: number
|
|
108
|
+
}> {
|
|
109
|
+
return (resource.spec?.groups || []).map((g: any) => {
|
|
110
|
+
const rules = g.rules || []
|
|
111
|
+
const alertCount = rules.filter((r: any) => r.alert).length
|
|
112
|
+
const recordCount = rules.filter((r: any) => r.record).length
|
|
113
|
+
return {
|
|
114
|
+
name: g.name,
|
|
115
|
+
interval: g.interval,
|
|
116
|
+
ruleCount: rules.length,
|
|
117
|
+
alertCount,
|
|
118
|
+
recordCount,
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// PODMONITOR UTILITIES
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
export function getPodMonitorStatus(resource: any): StatusBadge {
|
|
128
|
+
return getConditionStatus(resource)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function getPodMonitorEndpointCount(resource: any): number {
|
|
132
|
+
return (resource.spec?.podMetricsEndpoints || []).length
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function getPodMonitorSelector(resource: any): string {
|
|
136
|
+
return formatMatchLabels(resource.spec?.selector)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getPodMonitorEndpoints(resource: any): Array<{
|
|
140
|
+
port?: string
|
|
141
|
+
path?: string
|
|
142
|
+
interval?: string
|
|
143
|
+
scheme?: string
|
|
144
|
+
}> {
|
|
145
|
+
return (resource.spec?.podMetricsEndpoints || []).map((ep: any) => ({
|
|
146
|
+
port: ep.port || ep.targetPort,
|
|
147
|
+
path: ep.path || '/metrics',
|
|
148
|
+
interval: ep.interval,
|
|
149
|
+
scheme: ep.scheme,
|
|
150
|
+
}))
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getPodMonitorNamespaceSelector(resource: any): string {
|
|
154
|
+
const ns = resource.spec?.namespaceSelector
|
|
155
|
+
if (!ns) return 'Same namespace'
|
|
156
|
+
if (ns.any) return 'All namespaces'
|
|
157
|
+
if (ns.matchNames?.length) return ns.matchNames.join(', ')
|
|
158
|
+
return 'Same namespace'
|
|
159
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Traefik CRD utility functions for resource list cells and detail renderers
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// INGRESSROUTE UTILITIES
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export function getIngressRouteEntryPoints(resource: any): string {
|
|
8
|
+
const eps = resource.spec?.entryPoints || []
|
|
9
|
+
if (eps.length === 0) return '-'
|
|
10
|
+
if (eps.length <= 3) return eps.join(', ')
|
|
11
|
+
return `${eps.slice(0, 2).join(', ')} +${eps.length - 2} more`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getIngressRouteHosts(resource: any): string {
|
|
15
|
+
const routes = resource.spec?.routes || []
|
|
16
|
+
const hosts = new Set<string>()
|
|
17
|
+
for (const route of routes) {
|
|
18
|
+
const match = route.match || ''
|
|
19
|
+
// Extract Host(`...`) patterns from Traefik match expressions
|
|
20
|
+
const hostMatches = match.matchAll(/Host\(`([^`]+)`\)/gi)
|
|
21
|
+
for (const m of hostMatches) {
|
|
22
|
+
m[1].split(',').forEach((h: string) => {
|
|
23
|
+
const trimmed = h.trim()
|
|
24
|
+
if (trimmed) hosts.add(trimmed)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
// Also extract HostSNI(`...`) for TCP/TLS
|
|
28
|
+
const sniMatches = match.matchAll(/HostSNI\(`([^`]+)`\)/gi)
|
|
29
|
+
for (const m of sniMatches) {
|
|
30
|
+
m[1].split(',').forEach((h: string) => {
|
|
31
|
+
const trimmed = h.trim()
|
|
32
|
+
if (trimmed && trimmed !== '*') hosts.add(trimmed)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const arr = Array.from(hosts)
|
|
37
|
+
if (arr.length === 0) return '*'
|
|
38
|
+
if (arr.length <= 2) return arr.join(', ')
|
|
39
|
+
return `${arr[0]} +${arr.length - 1} more`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getIngressRouteRoutesSummary(resource: any): string {
|
|
43
|
+
const routes = resource.spec?.routes || []
|
|
44
|
+
if (routes.length === 0) return 'No routes'
|
|
45
|
+
|
|
46
|
+
const summaries: string[] = []
|
|
47
|
+
for (const route of routes) {
|
|
48
|
+
const services = route.services || []
|
|
49
|
+
const svcNames = services.map((s: any) => {
|
|
50
|
+
const kind = s.kind && s.kind !== 'Service' ? `${s.kind}/` : ''
|
|
51
|
+
const port = s.port ? `:${s.port}` : ''
|
|
52
|
+
return `${kind}${s.name}${port}`
|
|
53
|
+
})
|
|
54
|
+
if (svcNames.length > 0) {
|
|
55
|
+
summaries.push(svcNames.join(', '))
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (summaries.length === 0) return `${routes.length} route(s)`
|
|
60
|
+
if (summaries.length === 1) return summaries[0]
|
|
61
|
+
return `${summaries[0]}; +${summaries.length - 1} more`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function hasIngressRouteTLS(resource: any): boolean {
|
|
65
|
+
return !!resource.spec?.tls
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getIngressRouteMiddlewareCount(resource: any): number {
|
|
69
|
+
const routes = resource.spec?.routes || []
|
|
70
|
+
const seen = new Set<string>()
|
|
71
|
+
for (const route of routes) {
|
|
72
|
+
for (const mw of route.middlewares || []) {
|
|
73
|
+
const ns = mw.namespace || ''
|
|
74
|
+
seen.add(`${ns}/${mw.name}`)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return seen.size
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getIngressRouteServiceCount(resource: any): number {
|
|
81
|
+
const routes = resource.spec?.routes || []
|
|
82
|
+
const seen = new Set<string>()
|
|
83
|
+
for (const route of routes) {
|
|
84
|
+
for (const svc of route.services || []) {
|
|
85
|
+
const kind = svc.kind || 'Service'
|
|
86
|
+
const ns = svc.namespace || ''
|
|
87
|
+
seen.add(`${kind}/${ns}/${svc.name}`)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return seen.size
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// MIDDLEWARE UTILITIES
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
export function getMiddlewareType(resource: any): string {
|
|
98
|
+
const spec = resource.spec || {}
|
|
99
|
+
// Each middleware has exactly one spec key indicating its type
|
|
100
|
+
const types = [
|
|
101
|
+
'addPrefix', 'basicAuth', 'buffering', 'chain', 'circuitBreaker',
|
|
102
|
+
'compress', 'contentType', 'digestAuth', 'errors', 'forwardAuth',
|
|
103
|
+
'grpcWeb', 'headers', 'inFlightReq', 'ipAllowList', 'ipWhiteList',
|
|
104
|
+
'passTLSClientCert', 'plugin', 'rateLimit', 'redirectRegex',
|
|
105
|
+
'redirectScheme', 'replacePath', 'replacePathRegex', 'retry',
|
|
106
|
+
'stripPrefix', 'stripPrefixRegex',
|
|
107
|
+
]
|
|
108
|
+
for (const t of types) {
|
|
109
|
+
if (spec[t]) return t
|
|
110
|
+
}
|
|
111
|
+
return 'unknown'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getMiddlewareChainMembers(resource: any): string[] {
|
|
115
|
+
return (resource.spec?.chain?.middlewares || []).map((m: any) => m.name)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// TRAEFIKSERVICE UTILITIES
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
export function getTraefikServiceType(resource: any): string {
|
|
123
|
+
const spec = resource.spec || {}
|
|
124
|
+
if (spec.weighted) return 'Weighted Round Robin'
|
|
125
|
+
if (spec.mirroring) return 'Mirroring'
|
|
126
|
+
if (spec.highestRandomWeight) return 'Highest Random Weight'
|
|
127
|
+
return 'Unknown'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getTraefikServiceTargets(resource: any): string {
|
|
131
|
+
const spec = resource.spec || {}
|
|
132
|
+
if (spec.weighted) {
|
|
133
|
+
const svcs = spec.weighted.services || []
|
|
134
|
+
if (svcs.length === 0) return '-'
|
|
135
|
+
return svcs.map((s: any) => {
|
|
136
|
+
const w = s.weight !== undefined ? ` (${s.weight}%)` : ''
|
|
137
|
+
return `${s.name}${w}`
|
|
138
|
+
}).join(', ')
|
|
139
|
+
}
|
|
140
|
+
if (spec.mirroring) {
|
|
141
|
+
const primary = spec.mirroring.name || '-'
|
|
142
|
+
const mirrors = spec.mirroring.mirrors || []
|
|
143
|
+
if (mirrors.length === 0) return primary
|
|
144
|
+
return `${primary} + ${mirrors.length} mirror(s)`
|
|
145
|
+
}
|
|
146
|
+
if (spec.highestRandomWeight) {
|
|
147
|
+
const svcs = spec.highestRandomWeight.services || []
|
|
148
|
+
if (svcs.length === 0) return '-'
|
|
149
|
+
return svcs.map((s: any) => s.name).join(', ')
|
|
150
|
+
}
|
|
151
|
+
return '-'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// SERVERSTRANSPORT UTILITIES
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
export function getServersTransportInsecure(resource: any): boolean {
|
|
159
|
+
return !!resource.spec?.insecureSkipVerify
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function getServersTransportServerName(resource: any): string | null {
|
|
163
|
+
return resource.spec?.serverName || null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// TLSOPTION UTILITIES
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
export function getTLSOptionMinVersion(resource: any): string | null {
|
|
171
|
+
return resource.spec?.minVersion || null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function getTLSOptionMaxVersion(resource: any): string | null {
|
|
175
|
+
return resource.spec?.maxVersion || null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function getTLSOptionCipherSuites(resource: any): string[] {
|
|
179
|
+
return resource.spec?.cipherSuites || []
|
|
180
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Trivy Operator CRD utility functions — extracted from resource-utils.ts
|
|
2
|
+
|
|
3
|
+
import type { StatusBadge } from './resource-utils'
|
|
4
|
+
import { healthColors } from './resource-utils'
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// TRIVY OPERATOR — VULNERABILITY REPORT UTILITIES
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export interface VulnerabilitySummary {
|
|
11
|
+
critical: number
|
|
12
|
+
high: number
|
|
13
|
+
medium: number
|
|
14
|
+
low: number
|
|
15
|
+
unknown: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getVulnerabilityReportSummary(report: any): VulnerabilitySummary {
|
|
19
|
+
const summary = report.report?.summary || {}
|
|
20
|
+
return {
|
|
21
|
+
critical: summary.criticalCount || 0,
|
|
22
|
+
high: summary.highCount || 0,
|
|
23
|
+
medium: summary.mediumCount || 0,
|
|
24
|
+
low: summary.lowCount || 0,
|
|
25
|
+
unknown: summary.unknownCount || 0,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getVulnerabilityReportStatus(report: any): StatusBadge {
|
|
30
|
+
const summary = getVulnerabilityReportSummary(report)
|
|
31
|
+
if (summary.critical > 0) {
|
|
32
|
+
return { text: `${summary.critical} Critical`, color: healthColors.unhealthy, level: 'unhealthy' }
|
|
33
|
+
}
|
|
34
|
+
if (summary.high > 0) {
|
|
35
|
+
return { text: `${summary.high} High`, color: healthColors.degraded, level: 'degraded' }
|
|
36
|
+
}
|
|
37
|
+
if (summary.medium > 0) {
|
|
38
|
+
return { text: `${summary.medium} Medium`, color: healthColors.degraded, level: 'degraded' }
|
|
39
|
+
}
|
|
40
|
+
if (summary.low > 0 || summary.unknown > 0) {
|
|
41
|
+
return { text: 'Low', color: healthColors.neutral, level: 'neutral' }
|
|
42
|
+
}
|
|
43
|
+
return { text: 'Clean', color: healthColors.healthy, level: 'healthy' }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getVulnerabilityReportImage(report: any): string {
|
|
47
|
+
const artifact = report.report?.artifact || {}
|
|
48
|
+
const registry = report.report?.registry?.server || ''
|
|
49
|
+
if (!artifact.repository) return '-'
|
|
50
|
+
const repo = registry ? `${registry}/${artifact.repository}` : artifact.repository
|
|
51
|
+
return artifact.tag ? `${repo}:${artifact.tag}` : repo
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getVulnerabilityReportContainer(report: any): string {
|
|
55
|
+
return report.metadata?.labels?.['trivy-operator.container.name'] || '-'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// TRIVY OPERATOR — CONFIG AUDIT REPORT UTILITIES
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
export interface ConfigAuditSummary {
|
|
63
|
+
critical: number
|
|
64
|
+
high: number
|
|
65
|
+
medium: number
|
|
66
|
+
low: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getConfigAuditReportSummary(report: any): ConfigAuditSummary {
|
|
70
|
+
const summary = report.report?.summary || {}
|
|
71
|
+
return {
|
|
72
|
+
critical: summary.criticalCount || 0,
|
|
73
|
+
high: summary.highCount || 0,
|
|
74
|
+
medium: summary.mediumCount || 0,
|
|
75
|
+
low: summary.lowCount || 0,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getConfigAuditReportStatus(report: any): StatusBadge {
|
|
80
|
+
const summary = getConfigAuditReportSummary(report)
|
|
81
|
+
if (summary.critical > 0) {
|
|
82
|
+
return { text: `${summary.critical} Critical`, color: healthColors.unhealthy, level: 'unhealthy' }
|
|
83
|
+
}
|
|
84
|
+
if (summary.high > 0) {
|
|
85
|
+
return { text: `${summary.high} High`, color: healthColors.degraded, level: 'degraded' }
|
|
86
|
+
}
|
|
87
|
+
if (summary.medium > 0) {
|
|
88
|
+
return { text: `${summary.medium} Medium`, color: healthColors.degraded, level: 'degraded' }
|
|
89
|
+
}
|
|
90
|
+
if (summary.low > 0) {
|
|
91
|
+
return { text: 'Low', color: healthColors.neutral, level: 'neutral' }
|
|
92
|
+
}
|
|
93
|
+
return { text: 'Pass', color: healthColors.healthy, level: 'healthy' }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// TRIVY OPERATOR — EXPOSED SECRET REPORT UTILITIES
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
// ExposedSecretReport has the same summary/label structure as VulnerabilityReport
|
|
101
|
+
export const getExposedSecretReportSummary = getVulnerabilityReportSummary
|
|
102
|
+
export const getExposedSecretReportContainer = getVulnerabilityReportContainer
|
|
103
|
+
export const getExposedSecretReportImage = getVulnerabilityReportImage
|
|
104
|
+
|
|
105
|
+
export const getExposedSecretReportStatus = getVulnerabilityReportStatus
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// TRIVY OPERATOR — RBAC ASSESSMENT REPORT UTILITIES
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
// RbacAssessmentReport and ClusterRbacAssessmentReport have the same
|
|
112
|
+
// report.summary + report.checks[] structure as ConfigAuditReport
|
|
113
|
+
export const getRbacAssessmentReportSummary = getConfigAuditReportSummary
|
|
114
|
+
export const getRbacAssessmentReportStatus = getConfigAuditReportStatus
|
|
115
|
+
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// TRIVY OPERATOR — CLUSTER COMPLIANCE REPORT UTILITIES
|
|
118
|
+
// ============================================================================
|
|
119
|
+
|
|
120
|
+
export function getClusterComplianceReportStatus(report: any): StatusBadge {
|
|
121
|
+
const summary = report.status?.summary || {}
|
|
122
|
+
const fail = summary.failCount || 0
|
|
123
|
+
const pass = summary.passCount || 0
|
|
124
|
+
if (fail > 0) {
|
|
125
|
+
return { text: `${fail} Fail`, color: healthColors.unhealthy, level: 'unhealthy' }
|
|
126
|
+
}
|
|
127
|
+
if (pass > 0) {
|
|
128
|
+
return { text: 'Pass', color: healthColors.healthy, level: 'healthy' }
|
|
129
|
+
}
|
|
130
|
+
return { text: 'Pending', color: healthColors.neutral, level: 'neutral' }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// TRIVY OPERATOR — SBOM REPORT UTILITIES
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
export function getSbomReportStatus(report: any): StatusBadge {
|
|
138
|
+
const summary = report.report?.summary
|
|
139
|
+
if (summary == null) {
|
|
140
|
+
return { text: 'Pending', color: healthColors.neutral, level: 'neutral' }
|
|
141
|
+
}
|
|
142
|
+
const count = summary.componentsCount ?? 0
|
|
143
|
+
return { text: `${count} components`, color: healthColors.neutral, level: 'neutral' }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const getSbomReportContainer = getVulnerabilityReportContainer
|