@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.
Files changed (193) hide show
  1. package/package.json +63 -0
  2. package/src/components/dock/BottomDock.tsx +204 -0
  3. package/src/components/dock/DockContext.tsx +212 -0
  4. package/src/components/dock/TerminalTab.tsx +333 -0
  5. package/src/components/dock/index.ts +3 -0
  6. package/src/components/gitops/GitOpsActions.tsx +227 -0
  7. package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
  8. package/src/components/gitops/ManagedResourcesList.tsx +210 -0
  9. package/src/components/gitops/SyncCountdown.tsx +146 -0
  10. package/src/components/gitops/index.ts +4 -0
  11. package/src/components/logs/JsonLogLine.tsx +90 -0
  12. package/src/components/logs/LogCore.tsx +553 -0
  13. package/src/components/logs/LogToolbarSelects.tsx +74 -0
  14. package/src/components/logs/LogsViewer.tsx +146 -0
  15. package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
  16. package/src/components/logs/index.ts +13 -0
  17. package/src/components/logs/useLogBuffer.ts +138 -0
  18. package/src/components/logs/useLogSearch.ts +151 -0
  19. package/src/components/logs/useLogStream.ts +61 -0
  20. package/src/components/resources/ResourcesView.tsx +5494 -0
  21. package/src/components/resources/index.ts +17 -0
  22. package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
  23. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
  24. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
  25. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
  26. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
  27. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
  28. package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
  29. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
  30. package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
  31. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
  32. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
  33. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
  34. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
  35. package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
  36. package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
  37. package/src/components/resources/renderers/EventRenderer.tsx +175 -0
  38. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
  39. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
  40. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
  41. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
  42. package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
  43. package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
  44. package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
  45. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
  46. package/src/components/resources/renderers/HPARenderer.tsx +164 -0
  47. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
  48. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
  49. package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
  50. package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
  51. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
  52. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
  53. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
  54. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
  55. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
  56. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
  57. package/src/components/resources/renderers/JobRenderer.tsx +103 -0
  58. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
  59. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
  60. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
  61. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
  62. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
  63. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
  64. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
  65. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
  66. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
  67. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
  68. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
  69. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
  70. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
  71. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
  72. package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
  73. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
  74. package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
  75. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
  76. package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
  77. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
  78. package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
  79. package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
  80. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
  81. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
  82. package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
  83. package/src/components/resources/renderers/PodRenderer.tsx +677 -0
  84. package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
  85. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
  86. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
  87. package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
  88. package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
  89. package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
  90. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
  91. package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
  92. package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
  93. package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
  94. package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
  95. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
  96. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
  97. package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
  98. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
  99. package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
  100. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
  101. package/src/components/resources/renderers/VPARenderer.tsx +193 -0
  102. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
  103. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
  104. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
  105. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
  106. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
  107. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
  108. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
  109. package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
  110. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
  111. package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
  112. package/src/components/resources/renderers/argo-cells.tsx +118 -0
  113. package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
  114. package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
  115. package/src/components/resources/renderers/eso-cells.tsx +115 -0
  116. package/src/components/resources/renderers/flux-cells.tsx +253 -0
  117. package/src/components/resources/renderers/index.ts +105 -0
  118. package/src/components/resources/renderers/istio-cells.tsx +210 -0
  119. package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
  120. package/src/components/resources/renderers/keda-cells.tsx +94 -0
  121. package/src/components/resources/renderers/knative-cells.tsx +396 -0
  122. package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
  123. package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
  124. package/src/components/resources/renderers/traefik-cells.tsx +120 -0
  125. package/src/components/resources/renderers/trivy-cells.tsx +153 -0
  126. package/src/components/resources/renderers/trivy-shared.tsx +67 -0
  127. package/src/components/resources/renderers/velero-cells.tsx +164 -0
  128. package/src/components/resources/resource-utils-argo.ts +155 -0
  129. package/src/components/resources/resource-utils-certmanager.ts +198 -0
  130. package/src/components/resources/resource-utils-cnpg.ts +344 -0
  131. package/src/components/resources/resource-utils-eso.ts +385 -0
  132. package/src/components/resources/resource-utils-flux.ts +186 -0
  133. package/src/components/resources/resource-utils-istio.ts +372 -0
  134. package/src/components/resources/resource-utils-karpenter.ts +150 -0
  135. package/src/components/resources/resource-utils-keda.ts +203 -0
  136. package/src/components/resources/resource-utils-knative.ts +218 -0
  137. package/src/components/resources/resource-utils-kyverno.ts +224 -0
  138. package/src/components/resources/resource-utils-prometheus.ts +159 -0
  139. package/src/components/resources/resource-utils-traefik.ts +180 -0
  140. package/src/components/resources/resource-utils-trivy.ts +146 -0
  141. package/src/components/resources/resource-utils-velero.ts +293 -0
  142. package/src/components/resources/resource-utils.ts +1558 -0
  143. package/src/components/shared/EditableYamlView.tsx +302 -0
  144. package/src/components/shared/ResourceActionsBar.tsx +924 -0
  145. package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
  146. package/src/components/shared/index.ts +3 -0
  147. package/src/components/timeline/index.ts +1 -0
  148. package/src/components/timeline/shared.tsx +614 -0
  149. package/src/components/topology/GroupNode.tsx +198 -0
  150. package/src/components/topology/K8sResourceNode.tsx +440 -0
  151. package/src/components/topology/TopologyControls.tsx +62 -0
  152. package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
  153. package/src/components/topology/TopologyGraph.tsx +748 -0
  154. package/src/components/topology/TopologySearch.tsx +304 -0
  155. package/src/components/topology/index.ts +7 -0
  156. package/src/components/topology/layout.ts +734 -0
  157. package/src/components/topology/layout.worker.ts +272 -0
  158. package/src/components/topology/topology.css +129 -0
  159. package/src/components/ui/CodeViewer.tsx +451 -0
  160. package/src/components/ui/ConfirmDialog.tsx +188 -0
  161. package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
  162. package/src/components/ui/HealthRing.tsx +72 -0
  163. package/src/components/ui/MetricsChart.tsx +259 -0
  164. package/src/components/ui/ResourceBar.tsx +68 -0
  165. package/src/components/ui/Toast.tsx +225 -0
  166. package/src/components/ui/Tooltip.tsx +165 -0
  167. package/src/components/ui/YamlEditor.tsx +280 -0
  168. package/src/components/ui/drawer-components.tsx +792 -0
  169. package/src/components/ui/index.ts +10 -0
  170. package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
  171. package/src/components/workload/WorkloadView.tsx +1109 -0
  172. package/src/components/workload/index.ts +2 -0
  173. package/src/hooks/index.ts +9 -0
  174. package/src/hooks/useAnimatedUnmount.ts +30 -0
  175. package/src/hooks/useKeyboardShortcuts.tsx +336 -0
  176. package/src/hooks/useRefreshAnimation.ts +58 -0
  177. package/src/index.ts +35 -0
  178. package/src/types/core.ts +894 -0
  179. package/src/types/gitops.ts +458 -0
  180. package/src/types/index.ts +2 -0
  181. package/src/utils/animation.ts +40 -0
  182. package/src/utils/api-resources.ts +185 -0
  183. package/src/utils/badge-colors.ts +399 -0
  184. package/src/utils/context-name.ts +72 -0
  185. package/src/utils/download.ts +19 -0
  186. package/src/utils/format.ts +233 -0
  187. package/src/utils/index.ts +10 -0
  188. package/src/utils/log-format.ts +300 -0
  189. package/src/utils/navigation.ts +119 -0
  190. package/src/utils/resource-hierarchy.ts +630 -0
  191. package/src/utils/resource-icons.ts +206 -0
  192. package/src/vite-env.d.ts +1 -0
  193. package/tsconfig.json +24 -0
@@ -0,0 +1,203 @@
1
+ // KEDA CRD utility functions
2
+
3
+ import type { StatusBadge } from './resource-utils'
4
+ import { healthColors, formatAge } from './resource-utils'
5
+
6
+ // ============================================================================
7
+ // SHARED HELPERS
8
+ // ============================================================================
9
+
10
+ function extractTriggerTypes(resource: any): string {
11
+ const triggers = resource.spec?.triggers || []
12
+ if (triggers.length === 0) return '-'
13
+ const types = [...new Set(triggers.map((t: any) => t.type).filter(Boolean))] as string[]
14
+ if (types.length > 3) return `${types.slice(0, 3).join(', ')} +${types.length - 3}`
15
+ return types.join(', ') || '-'
16
+ }
17
+
18
+ // ============================================================================
19
+ // KEDA SCALEDOBJECT UTILITIES
20
+ // ============================================================================
21
+
22
+ export function getScaledObjectStatus(resource: any): StatusBadge {
23
+ const conditions = resource.status?.conditions || []
24
+
25
+ // Check for paused state (3 annotation variants)
26
+ const annotations = resource.metadata?.annotations || {}
27
+ const isPaused = annotations['autoscaling.keda.sh/paused'] === 'true' ||
28
+ annotations['autoscaling.keda.sh/paused-replicas'] !== undefined ||
29
+ conditions.some((c: any) => c.type === 'Paused' && c.status === 'True')
30
+
31
+ if (isPaused) {
32
+ return { text: 'Paused', color: healthColors.degraded, level: 'degraded' }
33
+ }
34
+
35
+ // Check Fallback condition
36
+ const fallbackCond = conditions.find((c: any) => c.type === 'Fallback')
37
+ if (fallbackCond?.status === 'True') {
38
+ return { text: 'Fallback', color: healthColors.unhealthy, level: 'unhealthy' }
39
+ }
40
+
41
+ // Check Ready condition
42
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
43
+ if (readyCond?.status === 'False') {
44
+ return { text: readyCond.reason || 'NotReady', color: healthColors.unhealthy, level: 'unhealthy' }
45
+ }
46
+
47
+ // Check Active condition
48
+ const activeCond = conditions.find((c: any) => c.type === 'Active')
49
+ if (activeCond?.status === 'True') {
50
+ return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
51
+ }
52
+ if (activeCond?.status === 'False') {
53
+ return { text: 'Idle', color: healthColors.degraded, level: 'degraded' }
54
+ }
55
+
56
+ if (readyCond?.status === 'True') {
57
+ return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
58
+ }
59
+
60
+ return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
61
+ }
62
+
63
+ export function getScaledObjectTarget(resource: any): string {
64
+ const ref = resource.spec?.scaleTargetRef
65
+ if (!ref) return '-'
66
+ return `${ref.kind || 'Deployment'}/${ref.name}`
67
+ }
68
+
69
+ export function getScaledObjectTargetKind(resource: any): string {
70
+ return resource.spec?.scaleTargetRef?.kind || 'Deployment'
71
+ }
72
+
73
+ export function getScaledObjectTargetName(resource: any): string {
74
+ return resource.spec?.scaleTargetRef?.name || '-'
75
+ }
76
+
77
+ export function getScaledObjectReplicas(resource: any): string {
78
+ const min = resource.spec?.minReplicaCount ?? 0
79
+ const max = resource.spec?.maxReplicaCount ?? '-'
80
+ const idle = resource.spec?.idleReplicaCount
81
+ const parts = [`${min}-${max}`]
82
+ if (idle !== undefined) parts.push(`idle: ${idle}`)
83
+ return parts.join(' ')
84
+ }
85
+
86
+ export function getScaledObjectTriggers(resource: any): Array<{ type: string; name?: string; metadata?: Record<string, string>; authenticationRef?: { name: string; kind?: string } }> {
87
+ return (resource.spec?.triggers || []).map((t: any) => ({
88
+ type: t.type,
89
+ name: t.name,
90
+ metadata: t.metadata,
91
+ ...(t.authenticationRef ? { authenticationRef: { name: t.authenticationRef.name, kind: t.authenticationRef.kind } } : {}),
92
+ }))
93
+ }
94
+
95
+ export function getScaledObjectTriggerTypes(resource: any): string {
96
+ return extractTriggerTypes(resource)
97
+ }
98
+
99
+ export function getScaledObjectTriggerCount(resource: any): number {
100
+ return (resource.spec?.triggers || []).length
101
+ }
102
+
103
+ export function getScaledObjectHpaName(resource: any): string {
104
+ return resource.status?.hpaName || '-'
105
+ }
106
+
107
+ export function getScaledObjectLastActiveTime(resource: any): string {
108
+ const lastActive = resource.status?.lastActiveTime
109
+ if (!lastActive) return '-'
110
+ return formatAge(lastActive)
111
+ }
112
+
113
+ export function getScaledObjectPollingInterval(resource: any): number {
114
+ return resource.spec?.pollingInterval ?? 30
115
+ }
116
+
117
+ export function getScaledObjectCooldownPeriod(resource: any): number {
118
+ return resource.spec?.cooldownPeriod ?? 300
119
+ }
120
+
121
+ // ============================================================================
122
+ // KEDA SCALEDJOB UTILITIES
123
+ // ============================================================================
124
+
125
+ export function getScaledJobStatus(resource: any): StatusBadge {
126
+ const conditions = resource.status?.conditions || []
127
+
128
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
129
+ if (readyCond?.status === 'True') {
130
+ return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
131
+ }
132
+
133
+ const activeCond = conditions.find((c: any) => c.type === 'Active')
134
+ if (activeCond?.status === 'True') {
135
+ return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
136
+ }
137
+ if (activeCond?.status === 'False') {
138
+ return { text: 'Idle', color: healthColors.degraded, level: 'degraded' }
139
+ }
140
+
141
+ if (readyCond?.status === 'False') {
142
+ return { text: readyCond.reason || 'NotReady', color: healthColors.unhealthy, level: 'unhealthy' }
143
+ }
144
+
145
+ return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
146
+ }
147
+
148
+ export function getScaledJobTarget(resource: any): string {
149
+ return resource.spec?.jobTargetRef?.name || '-'
150
+ }
151
+
152
+ export function getScaledJobStrategy(resource: any): string {
153
+ return resource.spec?.scalingStrategy?.strategy || 'default'
154
+ }
155
+
156
+ export function getScaledJobTriggerCount(resource: any): number {
157
+ return (resource.spec?.triggers || []).length
158
+ }
159
+
160
+ export function getScaledJobTriggers(resource: any): Array<{ type: string; name?: string; metadata?: Record<string, string>; authenticationRef?: { name: string; kind?: string } }> {
161
+ return (resource.spec?.triggers || []).map((t: any) => ({
162
+ type: t.type,
163
+ name: t.name,
164
+ metadata: t.metadata,
165
+ ...(t.authenticationRef ? { authenticationRef: { name: t.authenticationRef.name, kind: t.authenticationRef.kind } } : {}),
166
+ }))
167
+ }
168
+
169
+ export function getScaledJobTriggerTypes(resource: any): string {
170
+ return extractTriggerTypes(resource)
171
+ }
172
+
173
+ // ============================================================================
174
+ // KEDA TRIGGERAUTHENTICATION UTILITIES
175
+ // ============================================================================
176
+
177
+ export function getTriggerAuthSecretRefCount(resource: any): number {
178
+ return (resource.spec?.secretTargetRef || []).length
179
+ }
180
+
181
+ export function getTriggerAuthEnvCount(resource: any): number {
182
+ return (resource.spec?.env || []).length
183
+ }
184
+
185
+ export function getTriggerAuthHasVault(resource: any): boolean {
186
+ return !!resource.spec?.hashiCorpVault
187
+ }
188
+
189
+ export function getTriggerAuthSecretRefs(resource: any): Array<{ parameter: string; name: string; key: string }> {
190
+ return (resource.spec?.secretTargetRef || []).map((r: any) => ({
191
+ parameter: r.parameter || '',
192
+ name: r.name || '',
193
+ key: r.key || '',
194
+ }))
195
+ }
196
+
197
+ export function getTriggerAuthEnvVars(resource: any): Array<{ parameter: string; name: string; containerName?: string }> {
198
+ return (resource.spec?.env || []).map((e: any) => ({
199
+ parameter: e.parameter || '',
200
+ name: e.name || '',
201
+ containerName: e.containerName,
202
+ }))
203
+ }
@@ -0,0 +1,218 @@
1
+ // KNative 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
+ export function getKnativeConditionStatus(resource: any): StatusBadge {
11
+ const conditions = resource?.status?.conditions || []
12
+ const ready = conditions.find((c: any) => c.type === 'Ready')
13
+ if (!ready) return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
14
+ if (ready.status === 'True') return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
15
+ if (ready.status === 'False') return { text: ready.reason || 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
16
+ if (ready.status === 'Unknown') return { text: ready.reason || 'Reconciling', color: healthColors.degraded, level: 'degraded' }
17
+ return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
18
+ }
19
+
20
+ function formatTraffic(traffic: any[]): string {
21
+ if (!traffic || traffic.length === 0) return '-'
22
+ return traffic
23
+ .map((t: any) => {
24
+ const percent = t.percent != null ? `${t.percent}%` : ''
25
+ const target = t.revisionName || (t.latestRevision ? '(latest)' : '')
26
+ return [percent, target].filter(Boolean).join(' \u2192 ')
27
+ })
28
+ .join(', ')
29
+ }
30
+
31
+ function formatRef(ref: any): string {
32
+ if (!ref) return '-'
33
+ return `${ref.kind || ''}/${ref.name || ''}`
34
+ }
35
+
36
+ // ============================================================================
37
+ // KNATIVE SERVICE (serving.knative.dev/v1)
38
+ // ============================================================================
39
+
40
+ export function getKnativeServiceUrl(resource: any): string {
41
+ return resource?.status?.url || '-'
42
+ }
43
+
44
+ export function getKnativeServiceLatestRevision(resource: any): string {
45
+ return resource?.status?.latestReadyRevisionName || '-'
46
+ }
47
+
48
+ export function getKnativeServiceTraffic(resource: any): string {
49
+ return formatTraffic(resource?.status?.traffic)
50
+ }
51
+
52
+ // ============================================================================
53
+ // REVISION (serving.knative.dev/v1)
54
+ // ============================================================================
55
+
56
+ export function getRevisionStatus(resource: any): StatusBadge {
57
+ const conditions = resource?.status?.conditions || []
58
+ const ready = conditions.find((c: any) => c.type === 'Ready')
59
+ const active = conditions.find((c: any) => c.type === 'Active')
60
+
61
+ // Check for scaled-to-zero: Ready=True but Active=False with reason NoTraffic
62
+ if (ready?.status === 'True' && active?.status === 'False') {
63
+ const reason = active.reason || ''
64
+ if (reason === 'NoTraffic') {
65
+ return { text: 'Scaled to Zero', color: 'bg-blue-500/20 text-blue-400', level: 'healthy' }
66
+ }
67
+ return { text: reason || 'Inactive', color: 'bg-yellow-500/20 text-yellow-400', level: 'degraded' }
68
+ }
69
+
70
+ // Check for activating: Active condition is Unknown (scaling up)
71
+ if (ready?.status === 'True' && active?.status === 'Unknown') {
72
+ return { text: 'Activating', color: 'bg-blue-500/20 text-blue-400', level: 'healthy' }
73
+ }
74
+
75
+ return getKnativeConditionStatus(resource)
76
+ }
77
+
78
+ export function getRevisionImage(resource: any): string {
79
+ const containers = resource?.spec?.containers || resource?.spec?.template?.spec?.containers || []
80
+ return containers[0]?.image || '-'
81
+ }
82
+
83
+ export function getRevisionConcurrency(resource: any): string {
84
+ const cc = resource?.spec?.containerConcurrency
85
+ if (cc == null) return '-'
86
+ return cc === 0 ? 'Unlimited' : String(cc)
87
+ }
88
+
89
+ // ============================================================================
90
+ // ROUTE (serving.knative.dev/v1)
91
+ // ============================================================================
92
+
93
+ export function getRouteUrl(resource: any): string {
94
+ return resource?.status?.url || '-'
95
+ }
96
+
97
+ export function getRouteTraffic(resource: any): string {
98
+ return formatTraffic(resource?.spec?.traffic)
99
+ }
100
+
101
+ // ============================================================================
102
+ // CONFIGURATION (serving.knative.dev/v1)
103
+ // ============================================================================
104
+
105
+ export function getConfigurationLatestCreated(resource: any): string {
106
+ return resource?.status?.latestCreatedRevisionName || '-'
107
+ }
108
+
109
+ export function getConfigurationLatestReady(resource: any): string {
110
+ return resource?.status?.latestReadyRevisionName || '-'
111
+ }
112
+
113
+ // ============================================================================
114
+ // BROKER (eventing.knative.dev/v1)
115
+ // ============================================================================
116
+
117
+ export function getBrokerAddress(resource: any): string {
118
+ return resource?.status?.address?.url || '-'
119
+ }
120
+
121
+ // ============================================================================
122
+ // TRIGGER (eventing.knative.dev/v1)
123
+ // ============================================================================
124
+
125
+ export function getTriggerBroker(resource: any): string {
126
+ return resource?.spec?.broker || '-'
127
+ }
128
+
129
+ export function getTriggerSubscriber(resource: any): string {
130
+ const ref = resource?.spec?.subscriber?.ref
131
+ if (ref) return formatRef(ref)
132
+ return resource?.spec?.subscriber?.uri || '-'
133
+ }
134
+
135
+ export function getTriggerFilter(resource: any): string {
136
+ const attributes = resource?.spec?.filter?.attributes
137
+ if (!attributes || Object.keys(attributes).length === 0) return '-'
138
+ return Object.entries(attributes)
139
+ .map(([k, v]) => `${k}=${v}`)
140
+ .join(', ')
141
+ }
142
+
143
+ // ============================================================================
144
+ // SOURCES (sources.knative.dev/v1)
145
+ // ============================================================================
146
+
147
+ export function getSourceSink(resource: any): string {
148
+ const ref = resource?.spec?.sink?.ref
149
+ if (ref) return formatRef(ref)
150
+ return resource?.spec?.sink?.uri || '-'
151
+ }
152
+
153
+ export function getPingSourceSchedule(resource: any): string {
154
+ return resource?.spec?.schedule || '-'
155
+ }
156
+
157
+ export function getPingSourceData(resource: any): string {
158
+ const data = resource?.spec?.data
159
+ if (!data) return '-'
160
+ if (data.length > 50) return data.substring(0, 50) + '...'
161
+ return data
162
+ }
163
+
164
+ // ============================================================================
165
+ // CHANNEL (messaging.knative.dev/v1)
166
+ // ============================================================================
167
+
168
+ export function getChannelAddress(resource: any): string {
169
+ return resource?.status?.address?.url || '-'
170
+ }
171
+
172
+ // ============================================================================
173
+ // SUBSCRIPTION (messaging.knative.dev/v1)
174
+ // ============================================================================
175
+
176
+ export function getSubscriptionChannel(resource: any): string {
177
+ const channel = resource?.spec?.channel
178
+ if (!channel) return '-'
179
+ return formatRef(channel)
180
+ }
181
+
182
+ export function getSubscriptionSubscriber(resource: any): string {
183
+ const ref = resource?.spec?.subscriber?.ref
184
+ if (ref) return formatRef(ref)
185
+ return resource?.spec?.subscriber?.uri || '-'
186
+ }
187
+
188
+ // ============================================================================
189
+ // SEQUENCE (flows.knative.dev/v1)
190
+ // ============================================================================
191
+
192
+ export function getSequenceStepCount(resource: any): number {
193
+ return (resource?.spec?.steps || []).length
194
+ }
195
+
196
+ // ============================================================================
197
+ // PARALLEL (flows.knative.dev/v1)
198
+ // ============================================================================
199
+
200
+ export function getParallelBranchCount(resource: any): number {
201
+ return (resource?.spec?.branches || []).length
202
+ }
203
+
204
+ // ============================================================================
205
+ // DOMAINMAPPING (serving.knative.dev/v1beta1)
206
+ // ============================================================================
207
+
208
+ export function getDomainMappingUrl(resource: any): string {
209
+ return resource?.status?.url || '-'
210
+ }
211
+
212
+ // ============================================================================
213
+ // SERVERLESSSERVICE (networking.internal.knative.dev/v1alpha1)
214
+ // ============================================================================
215
+
216
+ export function getServerlessServiceMode(resource: any): string {
217
+ return resource?.spec?.mode || '-'
218
+ }
@@ -0,0 +1,224 @@
1
+ // Kyverno / Policy Report CRD utility functions
2
+
3
+ import type { StatusBadge } from './resource-utils'
4
+ import { healthColors, formatAge } from './resource-utils'
5
+
6
+ // ============================================================================
7
+ // SHARED HELPERS
8
+ // ============================================================================
9
+
10
+ interface PolicyReportSummary {
11
+ pass: number
12
+ fail: number
13
+ warn: number
14
+ error: number
15
+ skip: number
16
+ }
17
+
18
+ function extractSummary(resource: any): PolicyReportSummary {
19
+ const summary = resource.summary || {}
20
+ return {
21
+ pass: summary.pass ?? 0,
22
+ fail: summary.fail ?? 0,
23
+ warn: summary.warn ?? 0,
24
+ error: summary.error ?? 0,
25
+ skip: summary.skip ?? 0,
26
+ }
27
+ }
28
+
29
+ function extractResults(resource: any): any[] {
30
+ return resource.results || []
31
+ }
32
+
33
+ // ============================================================================
34
+ // POLICYREPORT UTILITIES (wgpolicyk8s.io/v1alpha2)
35
+ // ============================================================================
36
+
37
+ export function getPolicyReportStatus(resource: any): StatusBadge {
38
+ const summary = extractSummary(resource)
39
+
40
+ if (summary.error > 0) {
41
+ return { text: `${summary.error} Error`, color: healthColors.unhealthy, level: 'unhealthy' }
42
+ }
43
+ if (summary.fail > 0) {
44
+ return { text: `${summary.fail} Fail`, color: healthColors.unhealthy, level: 'unhealthy' }
45
+ }
46
+ if (summary.warn > 0) {
47
+ return { text: `${summary.warn} Warn`, color: healthColors.degraded, level: 'degraded' }
48
+ }
49
+ if (summary.pass > 0) {
50
+ return { text: 'Pass', color: healthColors.healthy, level: 'healthy' }
51
+ }
52
+ if (summary.skip > 0) {
53
+ return { text: 'Skip', color: healthColors.neutral, level: 'neutral' }
54
+ }
55
+ return { text: 'Empty', color: healthColors.unknown, level: 'unknown' }
56
+ }
57
+
58
+ export function getPolicyReportSummary(resource: any): PolicyReportSummary {
59
+ return extractSummary(resource)
60
+ }
61
+
62
+ export function getPolicyReportResults(resource: any): any[] {
63
+ return extractResults(resource)
64
+ }
65
+
66
+ export function getPolicyReportResultCount(resource: any): number {
67
+ return extractResults(resource).length
68
+ }
69
+
70
+ export function getPolicyReportScope(resource: any): string {
71
+ const scope = resource.scope
72
+ if (!scope) return '-'
73
+ const parts = [scope.kind, scope.namespace, scope.name].filter(Boolean)
74
+ return parts.join('/') || '-'
75
+ }
76
+
77
+ export function getPolicyReportSource(resource: any): string {
78
+ // Source may be on the report itself or on individual results
79
+ if (resource.source) return resource.source
80
+ const results = extractResults(resource)
81
+ if (results.length > 0 && results[0].source) return results[0].source
82
+ return '-'
83
+ }
84
+
85
+ // ============================================================================
86
+ // CLUSTERPOLICYREPORT UTILITIES (wgpolicyk8s.io/v1alpha2)
87
+ // ============================================================================
88
+
89
+ // ClusterPolicyReport has the same structure as PolicyReport (cluster-scoped)
90
+ export const getClusterPolicyReportStatus = getPolicyReportStatus
91
+ export const getClusterPolicyReportSummary = getPolicyReportSummary
92
+ export const getClusterPolicyReportResults = getPolicyReportResults
93
+ export const getClusterPolicyReportResultCount = getPolicyReportResultCount
94
+
95
+ // ============================================================================
96
+ // KYVERNO POLICY UTILITIES (kyverno.io/v1)
97
+ // ============================================================================
98
+
99
+ export function getKyvernoPolicyStatus(resource: any): StatusBadge {
100
+ const conditions = resource.status?.conditions || []
101
+
102
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
103
+ if (readyCond?.status === 'True') {
104
+ return { text: 'Ready', color: healthColors.healthy, level: 'healthy' }
105
+ }
106
+ if (readyCond?.status === 'False') {
107
+ return { text: readyCond.reason || 'Not Ready', color: healthColors.unhealthy, level: 'unhealthy' }
108
+ }
109
+
110
+ // Fallback: if spec exists, likely active
111
+ if (resource.spec?.rules?.length > 0) {
112
+ return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
113
+ }
114
+
115
+ return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
116
+ }
117
+
118
+ export function getKyvernoPolicyAction(resource: any): string {
119
+ // Check spec-level validationFailureAction (deprecated but still widely used)
120
+ const action = resource.spec?.validationFailureAction
121
+ if (action) return action
122
+ // Fallback: check first rule's validate.failureAction
123
+ const rules = resource.spec?.rules || []
124
+ for (const rule of rules) {
125
+ if (rule.validate?.failureAction) return rule.validate.failureAction
126
+ }
127
+ return 'Audit'
128
+ }
129
+
130
+ export function getKyvernoPolicyRuleCount(resource: any): number {
131
+ return (resource.spec?.rules || []).length
132
+ }
133
+
134
+ export function getKyvernoPolicyRuleTypes(resource: any): string {
135
+ const rules = resource.spec?.rules || []
136
+ const types = new Set<string>()
137
+ for (const rule of rules) {
138
+ if (rule.validate) types.add('validate')
139
+ if (rule.mutate) types.add('mutate')
140
+ if (rule.generate) types.add('generate')
141
+ if (rule.verifyImages) types.add('verifyImages')
142
+ }
143
+ if (types.size === 0) return '-'
144
+ return Array.from(types).join(', ')
145
+ }
146
+
147
+ export function getKyvernoPolicyRules(resource: any): Array<{
148
+ name: string
149
+ type: string
150
+ hasMatch: boolean
151
+ hasExclude: boolean
152
+ }> {
153
+ const rules = resource.spec?.rules || []
154
+ return rules.map((rule: any) => {
155
+ let type = 'unknown'
156
+ if (rule.validate) type = 'validate'
157
+ else if (rule.mutate) type = 'mutate'
158
+ else if (rule.generate) type = 'generate'
159
+ else if (rule.verifyImages) type = 'verifyImages'
160
+
161
+ return {
162
+ name: rule.name || '-',
163
+ type,
164
+ hasMatch: !!rule.match,
165
+ hasExclude: !!rule.exclude,
166
+ }
167
+ })
168
+ }
169
+
170
+ export function getKyvernoPolicyBackground(resource: any): boolean {
171
+ return resource.spec?.background !== false
172
+ }
173
+
174
+ export function getKyvernoPolicyRuleCountByType(resource: any): {
175
+ validate: number
176
+ mutate: number
177
+ generate: number
178
+ verifyImages: number
179
+ } {
180
+ // Use status rulecount if available, otherwise compute from rules
181
+ const statusCount = resource.status?.rulecount
182
+ if (statusCount) {
183
+ return {
184
+ validate: statusCount.validate ?? 0,
185
+ mutate: statusCount.mutate ?? 0,
186
+ generate: statusCount.generate ?? 0,
187
+ verifyImages: statusCount.verifyImages ?? 0,
188
+ }
189
+ }
190
+
191
+ const rules = resource.spec?.rules || []
192
+ const counts = { validate: 0, mutate: 0, generate: 0, verifyImages: 0 }
193
+ for (const rule of rules) {
194
+ if (rule.validate) counts.validate++
195
+ if (rule.mutate) counts.mutate++
196
+ if (rule.generate) counts.generate++
197
+ if (rule.verifyImages) counts.verifyImages++
198
+ }
199
+ return counts
200
+ }
201
+
202
+ export function getKyvernoPolicyAutogenRules(resource: any): string[] {
203
+ const autogen = resource.status?.autogen?.rules || []
204
+ return autogen.map((r: any) => r.name).filter(Boolean)
205
+ }
206
+
207
+ export function getKyvernoPolicyLastScheduleTime(resource: any): string {
208
+ const lastSchedule = resource.status?.lastScheduleTime
209
+ if (!lastSchedule) return '-'
210
+ return formatAge(lastSchedule)
211
+ }
212
+
213
+ // ============================================================================
214
+ // KYVERNO CLUSTERPOLICY UTILITIES (kyverno.io/v1)
215
+ // ============================================================================
216
+
217
+ // ClusterPolicy has the same structure as Policy (cluster-scoped)
218
+ export const getClusterPolicyStatus = getKyvernoPolicyStatus
219
+ export const getClusterPolicyAction = getKyvernoPolicyAction
220
+ export const getClusterPolicyRuleCount = getKyvernoPolicyRuleCount
221
+ export const getClusterPolicyRuleTypes = getKyvernoPolicyRuleTypes
222
+ export const getClusterPolicyRules = getKyvernoPolicyRules
223
+ export const getClusterPolicyBackground = getKyvernoPolicyBackground
224
+ export const getClusterPolicyRuleCountByType = getKyvernoPolicyRuleCountByType