@skyhook-io/k8s-ui 1.11.0 → 1.13.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 (123) hide show
  1. package/package.json +4 -4
  2. package/src/components/gitops/GitOpsStatusBadge.tsx +40 -0
  3. package/src/components/gitops/GitOpsTableView.tsx +22 -2
  4. package/src/components/gitops/ReconcilingIndicator.test.tsx +31 -0
  5. package/src/components/gitops/detail-helpers.ts +4 -1
  6. package/src/components/issues/severity.ts +1 -0
  7. package/src/components/resources/ResourcesView.empty-note.test.ts +41 -0
  8. package/src/components/resources/ResourcesView.tsx +412 -5
  9. package/src/components/resources/index.ts +27 -0
  10. package/src/components/resources/kyverno-legacy-action.test.ts +98 -0
  11. package/src/components/resources/renderers/AlertRenderer.tsx +4 -1
  12. package/src/components/resources/renderers/AnalysisRunRenderer.test.tsx +241 -0
  13. package/src/components/resources/renderers/AnalysisRunRenderer.tsx +166 -0
  14. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +31 -2
  15. package/src/components/resources/renderers/CNPGClusterRenderer.test.tsx +33 -0
  16. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +71 -4
  17. package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +243 -0
  18. package/src/components/resources/renderers/CNPGObjectStoreRenderer.test.tsx +99 -0
  19. package/src/components/resources/renderers/CNPGObjectStoreRenderer.tsx +235 -0
  20. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +11 -0
  21. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +23 -1
  22. package/src/components/resources/renderers/CNPGShared.tsx +27 -0
  23. package/src/components/resources/renderers/CalicoHostEndpointRenderer.tsx +71 -0
  24. package/src/components/resources/renderers/CalicoIPPoolRenderer.tsx +32 -0
  25. package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.test.tsx +205 -0
  26. package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.tsx +537 -0
  27. package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.test.tsx +223 -0
  28. package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.tsx +670 -0
  29. package/src/components/resources/renderers/CalicoTierRenderer.tsx +19 -0
  30. package/src/components/resources/renderers/CompositeRenderer.test.tsx +81 -0
  31. package/src/components/resources/renderers/CompositeRenderer.tsx +67 -7
  32. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +4 -1
  33. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +4 -1
  34. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +4 -1
  35. package/src/components/resources/renderers/KustomizationRenderer.tsx +4 -1
  36. package/src/components/resources/renderers/KyvernoCELPolicyRenderers.tsx +13 -6
  37. package/src/components/resources/renderers/KyvernoExceptionRenderers.tsx +3 -2
  38. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.test.ts +31 -0
  39. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +124 -0
  40. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +28 -2
  41. package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +303 -0
  42. package/src/components/resources/renderers/LookupFailureNote.test.tsx +60 -0
  43. package/src/components/resources/renderers/LookupFailureNote.tsx +42 -0
  44. package/src/components/resources/renderers/NetworkPolicyDiagram.tsx +27 -26
  45. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -3
  46. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +4 -1
  47. package/src/components/resources/renderers/PodRenderer.tsx +13 -0
  48. package/src/components/resources/renderers/PolicyCoverageSection.test.tsx +348 -0
  49. package/src/components/resources/renderers/PolicyCoverageSection.tsx +939 -0
  50. package/src/components/resources/renderers/PolicySection.test.tsx +159 -0
  51. package/src/components/resources/renderers/PolicySection.tsx +197 -0
  52. package/src/components/resources/renderers/RolloutRenderer.test.tsx +491 -0
  53. package/src/components/resources/renderers/RolloutRenderer.tsx +391 -92
  54. package/src/components/resources/renderers/VeleroBSLRenderer.test.tsx +374 -0
  55. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +179 -3
  56. package/src/components/resources/renderers/VeleroBackupRenderer.test.tsx +221 -0
  57. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +119 -6
  58. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.test.tsx +69 -0
  59. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +96 -0
  60. package/src/components/resources/renderers/VeleroRestoreRenderer.test.tsx +55 -0
  61. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +22 -4
  62. package/src/components/resources/renderers/VeleroRunMessages.test.tsx +152 -0
  63. package/src/components/resources/renderers/VeleroRunMessages.tsx +139 -0
  64. package/src/components/resources/renderers/VeleroScheduleRenderer.test.tsx +88 -0
  65. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +16 -6
  66. package/src/components/resources/renderers/WorkloadRenderer.tsx +15 -1
  67. package/src/components/resources/renderers/calico-cells.tsx +177 -0
  68. package/src/components/resources/renderers/cnpg-cells.tsx +100 -0
  69. package/src/components/resources/renderers/index.ts +16 -0
  70. package/src/components/resources/renderers/kyverno-cells.tsx +90 -0
  71. package/src/components/resources/renderers/network-policy-peer-styles.ts +25 -0
  72. package/src/components/resources/renderers/policyreport-subject.test.tsx +27 -0
  73. package/src/components/resources/renderers/velero-cells.tsx +29 -7
  74. package/src/components/resources/renderers/velero-dead-ends.test.tsx +142 -0
  75. package/src/components/resources/resource-utils-calico.test.ts +215 -0
  76. package/src/components/resources/resource-utils-calico.ts +261 -0
  77. package/src/components/resources/resource-utils-cnpg-declarative.test.ts +104 -0
  78. package/src/components/resources/resource-utils-cnpg-objectstore.test.ts +122 -0
  79. package/src/components/resources/resource-utils-cnpg.test.ts +117 -0
  80. package/src/components/resources/resource-utils-cnpg.ts +324 -3
  81. package/src/components/resources/resource-utils-crossplane.test.ts +68 -0
  82. package/src/components/resources/resource-utils-kyverno-modern.ts +24 -0
  83. package/src/components/resources/resource-utils-kyverno-queue.test.ts +164 -0
  84. package/src/components/resources/resource-utils-kyverno-queue.ts +198 -0
  85. package/src/components/resources/resource-utils-kyverno.ts +43 -7
  86. package/src/components/resources/resource-utils-velero.test.ts +26 -0
  87. package/src/components/resources/resource-utils-velero.ts +68 -0
  88. package/src/components/resources/resource-utils.ts +98 -0
  89. package/src/components/shared/ResourceActionsBar.tsx +101 -16
  90. package/src/components/shared/ResourceRendererDispatch.test.tsx +379 -3
  91. package/src/components/shared/ResourceRendererDispatch.tsx +199 -22
  92. package/src/components/shared/index.ts +1 -1
  93. package/src/components/shared/revision-role-badges.test.ts +120 -0
  94. package/src/components/topology/K8sResourceNode.test.ts +10 -0
  95. package/src/components/topology/K8sResourceNode.tsx +22 -0
  96. package/src/components/topology/TopologyFilterSidebar.test.tsx +31 -0
  97. package/src/components/topology/TopologyFilterSidebar.tsx +5 -0
  98. package/src/components/topology/TopologyGraph.tsx +6 -6
  99. package/src/components/topology/TopologySearch.tsx +7 -0
  100. package/src/components/topology/layout.ts +3 -0
  101. package/src/components/trace/ReachabilityView.tsx +1 -0
  102. package/src/components/trace/TracePanel.tsx +12 -2
  103. package/src/components/trace/reachFixtures.ts +26 -1
  104. package/src/components/trace/reachGraphModel.test.ts +20 -0
  105. package/src/components/trace/reachGraphModel.ts +8 -1
  106. package/src/components/trace/reachOrigins.test.ts +31 -0
  107. package/src/components/trace/reachOrigins.ts +11 -1
  108. package/src/components/ui/Badge.tsx +10 -0
  109. package/src/components/ui/ScheduleValue.test.tsx +80 -0
  110. package/src/components/ui/ScheduleValue.tsx +85 -0
  111. package/src/components/ui/drawer-components.test.tsx +17 -1
  112. package/src/components/ui/drawer-components.tsx +9 -2
  113. package/src/components/ui/index.ts +2 -0
  114. package/src/components/workload/WorkloadView.tsx +3 -0
  115. package/src/types/core.ts +29 -0
  116. package/src/types/gitops-flux-status.test.ts +163 -0
  117. package/src/types/gitops.ts +80 -24
  118. package/src/types/index.ts +1 -0
  119. package/src/types/policy.ts +193 -0
  120. package/src/utils/navigation.test.ts +5 -0
  121. package/src/utils/navigation.ts +5 -0
  122. package/src/utils/resource-icons.test.ts +49 -2
  123. package/src/utils/resource-icons.ts +29 -2
@@ -0,0 +1,177 @@
1
+ import { Badge } from '../../ui/Badge'
2
+ import { Tooltip } from '../../ui/Tooltip'
3
+ import {
4
+ getCalicoPolicyNamespaceSelector,
5
+ getCalicoPolicyRuleCount,
6
+ getCalicoPolicySelector,
7
+ getCalicoPolicyServiceAccountSelector,
8
+ getCalicoPolicyTypes,
9
+ getCalicoIPPoolAllowedUses,
10
+ getCalicoIPPoolBlockSize,
11
+ getCalicoIPPoolEncapsulation,
12
+ isCalicoApiVersion,
13
+ isCalicoPolicyResource,
14
+ } from '../resource-utils-calico'
15
+
16
+ export function CalicoPolicyCell({
17
+ resource,
18
+ column,
19
+ }: {
20
+ resource: any
21
+ column: string
22
+ }) {
23
+ if (!isCalicoPolicyResource(resource))
24
+ return <span className="text-sm text-theme-text-tertiary">-</span>
25
+
26
+ switch (column) {
27
+ case 'selector':
28
+ return <SelectorCell value={getCalicoPolicySelector(resource)} />
29
+ case 'namespaceSelector':
30
+ return <SelectorCell value={getCalicoPolicyNamespaceSelector(resource)} />
31
+ case 'serviceAccountSelector':
32
+ return (
33
+ <SelectorCell value={getCalicoPolicyServiceAccountSelector(resource)} />
34
+ )
35
+ case 'tier':
36
+ return (
37
+ <span className="text-sm text-theme-text-secondary">
38
+ {resource.spec?.tier || 'default'}
39
+ </span>
40
+ )
41
+ case 'order':
42
+ return (
43
+ <span className="text-sm text-theme-text-secondary">
44
+ {resource.spec?.order ?? '-'}
45
+ </span>
46
+ )
47
+ case 'types':
48
+ return (
49
+ <div className="flex flex-wrap gap-1">
50
+ {getCalicoPolicyTypes(resource).map((type) => (
51
+ <Badge key={type} tone="accent1" size="sm">
52
+ {type}
53
+ </Badge>
54
+ ))}
55
+ </div>
56
+ )
57
+ case 'stagedAction':
58
+ return resource.spec?.stagedAction ? (
59
+ <Badge tone="note" size="sm">
60
+ {String(resource.spec.stagedAction)}
61
+ </Badge>
62
+ ) : (
63
+ <span className="text-sm text-theme-text-tertiary">-</span>
64
+ )
65
+ case 'rules': {
66
+ const { ingress, egress } = getCalicoPolicyRuleCount(resource)
67
+ return (
68
+ <span className="text-sm text-theme-text-secondary">
69
+ {ingress}i / {egress}e
70
+ </span>
71
+ )
72
+ }
73
+ default:
74
+ return <span className="text-sm text-theme-text-tertiary">-</span>
75
+ }
76
+ }
77
+
78
+ function SelectorCell({ value }: { value: string }) {
79
+ return (
80
+ <Tooltip content={value}>
81
+ <span className="text-sm text-theme-text-secondary truncate block font-mono">
82
+ {value}
83
+ </span>
84
+ </Tooltip>
85
+ )
86
+ }
87
+
88
+ /**
89
+ * List cells for the Calico resources that describe the network itself rather
90
+ * than policy: IPPool, HostEndpoint and Tier. The values that decide whether a
91
+ * pool is in use — disabled, encapsulation, node selector — belong in the list,
92
+ * not only behind a click on each row.
93
+ */
94
+ export function CalicoInfraCell({
95
+ resource,
96
+ column,
97
+ }: {
98
+ resource: any
99
+ column: string
100
+ }) {
101
+ if (!isCalicoApiVersion(resource?.apiVersion))
102
+ return <span className="text-sm text-theme-text-tertiary">-</span>
103
+
104
+ const spec = resource?.spec ?? {}
105
+ switch (column) {
106
+ case 'cidr':
107
+ return <MonoCell value={spec.cidr} />
108
+ case 'blockSize':
109
+ return <TextCell value={getCalicoIPPoolBlockSize(resource)} />
110
+ case 'encapsulation':
111
+ return <TextCell value={getCalicoIPPoolEncapsulation(resource)} />
112
+ case 'natOutgoing':
113
+ return <YesNoCell value={spec.natOutgoing === true} />
114
+ case 'disabled':
115
+ return spec.disabled === true ? (
116
+ <Badge severity="warning" size="sm">
117
+ Disabled
118
+ </Badge>
119
+ ) : (
120
+ <span className="text-sm text-theme-text-secondary">No</span>
121
+ )
122
+ case 'allowedUses':
123
+ return <TextCell value={getCalicoIPPoolAllowedUses(resource)} />
124
+ case 'nodeSelector':
125
+ return <MonoCell value={spec.nodeSelector ?? 'all()'} />
126
+ case 'node':
127
+ return <TextCell value={spec.node} />
128
+ case 'interfaceName':
129
+ return <MonoCell value={spec.interfaceName} />
130
+ case 'expectedIPs':
131
+ return (
132
+ <MonoCell
133
+ value={
134
+ Array.isArray(spec.expectedIPs)
135
+ ? spec.expectedIPs.join(', ')
136
+ : undefined
137
+ }
138
+ />
139
+ )
140
+ case 'profiles':
141
+ return (
142
+ <TextCell
143
+ value={
144
+ Array.isArray(spec.profiles) ? spec.profiles.join(', ') : undefined
145
+ }
146
+ />
147
+ )
148
+ case 'order':
149
+ return <TextCell value={spec.order} />
150
+ case 'defaultAction':
151
+ return <TextCell value={spec.defaultAction ?? 'Deny'} />
152
+ default:
153
+ return <span className="text-sm text-theme-text-tertiary">-</span>
154
+ }
155
+ }
156
+
157
+ function TextCell({ value }: { value: unknown }) {
158
+ if (value === undefined || value === null || value === '')
159
+ return <span className="text-sm text-theme-text-tertiary">-</span>
160
+ return (
161
+ <span className="text-sm text-theme-text-secondary">{String(value)}</span>
162
+ )
163
+ }
164
+
165
+ function MonoCell({ value }: { value: unknown }) {
166
+ if (value === undefined || value === null || value === '')
167
+ return <span className="text-sm text-theme-text-tertiary">-</span>
168
+ return <SelectorCell value={String(value)} />
169
+ }
170
+
171
+ function YesNoCell({ value }: { value: boolean }) {
172
+ return (
173
+ <span className="text-sm text-theme-text-secondary">
174
+ {value ? 'Yes' : 'No'}
175
+ </span>
176
+ )
177
+ }
@@ -24,6 +24,14 @@ import {
24
24
  getCNPGPoolerType,
25
25
  getCNPGPoolerMode,
26
26
  getCNPGPoolerInstances,
27
+ getCNPGObjectStoreStatus,
28
+ getCNPGObjectStoreDestination,
29
+ getCNPGObjectStoreRetention,
30
+ getCNPGObjectStoreRecoveryWindows,
31
+ getCNPGDeclarativeStatus,
32
+ getCNPGDeclarativeCluster,
33
+ getCNPGDeclarativeMessage,
34
+ getCNPGImageCatalogEntries,
27
35
  } from '../resource-utils-cnpg'
28
36
 
29
37
  /** Terminal = the operator has stopped reconciling; neighbouring cells are stale. */
@@ -122,6 +130,98 @@ export function CNPGBackupCell({ resource, column }: { resource: any; column: st
122
130
  }
123
131
  }
124
132
 
133
+ export function CNPGDeclarativeCell({ resource, column }: { resource: any; column: string }) {
134
+ switch (column) {
135
+ case 'status': {
136
+ const status = getCNPGDeclarativeStatus(resource)
137
+ return <span className={clsx('badge', status.color)}>{status.text}</span>
138
+ }
139
+ case 'cluster':
140
+ return (
141
+ <span className="text-sm text-theme-text-secondary truncate block">
142
+ {getCNPGDeclarativeCluster(resource) || '-'}
143
+ </span>
144
+ )
145
+ case 'target':
146
+ return (
147
+ <span className="text-sm text-theme-text-secondary truncate block">
148
+ {resource?.spec?.name || '-'}
149
+ </span>
150
+ )
151
+ case 'dbname':
152
+ return (
153
+ <span className="text-sm text-theme-text-secondary truncate block">
154
+ {resource?.spec?.dbname || resource?.spec?.owner || '-'}
155
+ </span>
156
+ )
157
+ case 'message':
158
+ return (
159
+ <span
160
+ className="text-sm text-theme-text-secondary truncate block"
161
+ title={getCNPGDeclarativeMessage(resource) || ''}
162
+ >
163
+ {getCNPGDeclarativeMessage(resource) || '-'}
164
+ </span>
165
+ )
166
+ default:
167
+ return <span className="text-sm text-theme-text-tertiary">-</span>
168
+ }
169
+ }
170
+
171
+ export function CNPGImageCatalogCell({ resource, column }: { resource: any; column: string }) {
172
+ const entries = getCNPGImageCatalogEntries(resource)
173
+ switch (column) {
174
+ case 'majors':
175
+ return (
176
+ <span className="text-sm text-theme-text-secondary">
177
+ {entries.length === 0 ? 'none' : entries.map((e) => e.major).join(', ')}
178
+ </span>
179
+ )
180
+ case 'images':
181
+ return <span className="text-sm text-theme-text-secondary">{String(entries.length)}</span>
182
+ default:
183
+ return <span className="text-sm text-theme-text-tertiary">-</span>
184
+ }
185
+ }
186
+
187
+ export function CNPGObjectStoreCell({ resource, column }: { resource: any; column: string }) {
188
+ switch (column) {
189
+ case 'status': {
190
+ const status = getCNPGObjectStoreStatus(resource)
191
+ return <span className={clsx('badge', status.color)}>{status.text}</span>
192
+ }
193
+ case 'destination':
194
+ // `block` is load-bearing: an inline span with `truncate` establishes no
195
+ // box, so the column collapses to almost nothing regardless of its width.
196
+ // Every sibling cell that renders a long value pairs the two.
197
+ return (
198
+ <span className="text-sm text-theme-text-secondary truncate block">
199
+ {getCNPGObjectStoreDestination(resource)}
200
+ </span>
201
+ )
202
+ case 'retention':
203
+ return (
204
+ <span className="text-sm text-theme-text-secondary">
205
+ {getCNPGObjectStoreRetention(resource) || '-'}
206
+ </span>
207
+ )
208
+ case 'servers': {
209
+ const windows = getCNPGObjectStoreRecoveryWindows(resource)
210
+ if (windows.length === 0) {
211
+ return <span className="text-sm text-theme-text-tertiary">none</span>
212
+ }
213
+ const failing = windows.filter((w) => w.failingSinceLastSuccess).length
214
+ return (
215
+ <span className="text-sm text-theme-text-secondary">
216
+ {failing > 0 ? `${windows.length} (${failing} failing)` : String(windows.length)}
217
+ </span>
218
+ )
219
+ }
220
+ default:
221
+ return <span className="text-sm text-theme-text-tertiary">-</span>
222
+ }
223
+ }
224
+
125
225
  export function CNPGScheduledBackupCell({ resource, column }: { resource: any; column: string }) {
126
226
  switch (column) {
127
227
  case 'status': {
@@ -4,6 +4,12 @@ export * from './argo-cells'
4
4
  export * from './CertificateRenderer'
5
5
  export * from './CertificateRequestRenderer'
6
6
  export * from './certmanager-cells'
7
+ export * from './CalicoHostEndpointRenderer'
8
+ export * from './CalicoIPPoolRenderer'
9
+ export * from './CalicoNetworkPolicyRenderer'
10
+ export * from './CalicoNetworkPolicyDiagram'
11
+ export * from './CalicoTierRenderer'
12
+ export * from './calico-cells'
7
13
  export * from './ChallengeRenderer'
8
14
  export * from './ClusterComplianceReportRenderer'
9
15
  export * from './ClusterExternalSecretRenderer'
@@ -74,6 +80,7 @@ export * from './KyvernoPolicyShared'
74
80
  export * from './KyvernoCELPolicyRenderers'
75
81
  export * from './KyvernoExceptionRenderers'
76
82
  export * from './LeaseRenderer'
83
+ export * from './LookupFailureNote'
77
84
  export * from './NetworkPolicyRenderer'
78
85
  export * from './NetworkPolicyDiagram'
79
86
  export * from './CiliumNetworkPolicyRenderer'
@@ -94,6 +101,7 @@ export * from './ReplicaSetRenderer'
94
101
  export * from './RoleBindingRenderer'
95
102
  export * from './RoleRenderer'
96
103
  export * from './RolloutRenderer'
104
+ export * from './AnalysisRunRenderer'
97
105
  export * from './RuntimeClassRenderer'
98
106
  export * from './SbomReportRenderer'
99
107
  export * from './SealedSecretRenderer'
@@ -109,10 +117,12 @@ export * from './trivy-cells'
109
117
  export * from './trivy-shared'
110
118
  export * from './velero-cells'
111
119
  export * from './VeleroBackupRenderer'
120
+ export * from './VeleroRunMessages'
112
121
  export * from './VeleroBSLRenderer'
113
122
  export * from './VeleroRestoreRenderer'
114
123
  export * from './VeleroScheduleRenderer'
115
124
  export * from './VeleroVSLRenderer'
125
+ export * from './VeleroBackupRepositoryRenderer'
116
126
  export * from './VPARenderer'
117
127
  export * from './VulnerabilityReportRenderer'
118
128
  export * from './WebhookConfigRenderer'
@@ -160,3 +170,9 @@ export * from './ResourceSliceRenderer'
160
170
  export * from './nvidia-cells'
161
171
  export * from './NvidiaClusterPolicyRenderer'
162
172
  export * from './NvidiaDriverRenderer'
173
+ export * from './PolicySection'
174
+ export * from './PolicyCoverageSection'
175
+ export * from './KyvernoGlobalContextRenderer'
176
+ export * from './KyvernoQueueRenderers'
177
+ export * from './CNPGObjectStoreRenderer'
178
+ export * from './CNPGDeclarativeRenderer'
@@ -2,12 +2,23 @@
2
2
 
3
3
  import { clsx } from 'clsx'
4
4
  import {
5
+ getPolicyReportScope,
5
6
  getPolicyReportStatus,
6
7
  getPolicyReportSummary,
7
8
  getKyvernoPolicyStatus,
8
9
  getKyvernoPolicyAction,
9
10
  getKyvernoPolicyRuleCount,
10
11
  } from '../resource-utils-kyverno'
12
+ import {
13
+ getKyvernoRequestState,
14
+ getKyvernoRequestType,
15
+ getKyvernoRequestPolicy,
16
+ getKyvernoRequestTriggers,
17
+ getKyvernoReportStatus,
18
+ getKyvernoReportSubject,
19
+ getKyvernoReportSource,
20
+ getKyvernoReportSummary,
21
+ } from '../resource-utils-kyverno-queue'
11
22
 
12
23
  export function PolicyReportCell({ resource, column }: { resource: any; column: string }) {
13
24
  switch (column) {
@@ -19,6 +30,14 @@ export function PolicyReportCell({ resource, column }: { resource: any; column:
19
30
  </span>
20
31
  )
21
32
  }
33
+ // Kyverno names a per-resource report after the subject's UID, so the Name
34
+ // column reads as a bare UUID. Without the subject the whole table is
35
+ // unidentifiable rows with counts beside them — you cannot tell which of
36
+ // your workloads a failing report belongs to without opening every one.
37
+ case 'scope': {
38
+ const scope = getPolicyReportScope(resource)
39
+ return <span className="truncate text-theme-text-primary" title={scope}>{scope}</span>
40
+ }
22
41
  case 'pass': {
23
42
  const summary = getPolicyReportSummary(resource)
24
43
  return <span className={clsx('text-sm', summary.pass > 0 ? 'text-green-400' : 'text-theme-text-tertiary')}>{summary.pass}</span>
@@ -83,3 +102,74 @@ export function ClusterPolicyCell({ resource, column }: { resource: any; column:
83
102
  // Same rendering logic as KyvernoPolicy
84
103
  return <KyvernoPolicyCell resource={resource} column={column} />
85
104
  }
105
+
106
+ // ============================================================================
107
+ // UpdateRequest / EphemeralReport
108
+ // ============================================================================
109
+
110
+ export function KyvernoUpdateRequestCell({ resource, column }: { resource: any; column: string }) {
111
+ switch (column) {
112
+ case 'status': {
113
+ const state = getKyvernoRequestState(resource)
114
+ return <span className={clsx('badge', state.color)}>{state.text}</span>
115
+ }
116
+ case 'type': {
117
+ const t = getKyvernoRequestType(resource)
118
+ return <span className="text-sm text-theme-text-secondary">{t === 'unknown' ? '-' : t}</span>
119
+ }
120
+ case 'policy':
121
+ return (
122
+ <span className="text-sm text-theme-text-secondary truncate block" title={getKyvernoRequestPolicy(resource)}>
123
+ {getKyvernoRequestPolicy(resource) || '-'}
124
+ </span>
125
+ )
126
+ case 'triggers': {
127
+ // A generate request carries every trigger it was queued for, so the row
128
+ // has to say "and N more" rather than pretend it is about one resource.
129
+ const triggers = getKyvernoRequestTriggers(resource)
130
+ if (triggers.length === 0) return <span className="text-sm text-theme-text-tertiary">-</span>
131
+ const first = triggers[0]
132
+ const label = `${first.kind ?? ''}/${first.name ?? ''}`
133
+ return (
134
+ <span className="text-sm text-theme-text-secondary truncate block" title={label}>
135
+ {triggers.length === 1 ? label : `${label} +${triggers.length - 1}`}
136
+ </span>
137
+ )
138
+ }
139
+ default:
140
+ return <span className="text-sm text-theme-text-tertiary">-</span>
141
+ }
142
+ }
143
+
144
+ export function KyvernoEphemeralReportCell({ resource, column }: { resource: any; column: string }) {
145
+ switch (column) {
146
+ case 'status': {
147
+ const status = getKyvernoReportStatus(resource)
148
+ return <span className={clsx('badge', status.color)}>{status.text}</span>
149
+ }
150
+ case 'subject': {
151
+ const s = getKyvernoReportSubject(resource)
152
+ if (!s?.kind) return <span className="text-sm text-theme-text-tertiary">-</span>
153
+ const label = s.name ? `${s.kind}/${s.name}` : `${s.kind} (deleted)`
154
+ return (
155
+ <span className="text-sm text-theme-text-secondary truncate block" title={label}>
156
+ {label}
157
+ </span>
158
+ )
159
+ }
160
+ case 'source':
161
+ return (
162
+ <span className="text-sm text-theme-text-secondary truncate block">
163
+ {(getKyvernoReportSource(resource) || '-').replace(/-/g, ' ')}
164
+ </span>
165
+ )
166
+ case 'results':
167
+ return (
168
+ <span className="text-sm text-theme-text-secondary">
169
+ {String(getKyvernoReportSummary(resource).total)}
170
+ </span>
171
+ )
172
+ default:
173
+ return <span className="text-sm text-theme-text-tertiary">-</span>
174
+ }
175
+ }
@@ -0,0 +1,25 @@
1
+ export type NetworkPolicyPeerType =
2
+ | "pod"
3
+ | "namespace"
4
+ | "cidr"
5
+ | "all"
6
+ | "deny"
7
+ | "combined";
8
+
9
+ export const NETWORK_POLICY_PEER_STYLES = {
10
+ pod: "border-emerald-500/30 bg-emerald-500/8",
11
+ namespace: "border-sky-500/30 bg-sky-500/8",
12
+ cidr: "border-amber-500/30 bg-amber-500/8",
13
+ combined: "border-emerald-500/30 bg-emerald-500/8",
14
+ all: "border-theme-border bg-theme-elevated/50",
15
+ deny: "border-red-500/30 bg-red-500/8",
16
+ } as const satisfies Record<NetworkPolicyPeerType, string>;
17
+
18
+ export const NETWORK_POLICY_PEER_DOTS = {
19
+ pod: "bg-emerald-500",
20
+ namespace: "bg-sky-500",
21
+ cidr: "bg-amber-500",
22
+ combined: "bg-emerald-500",
23
+ all: "bg-theme-text-tertiary",
24
+ deny: "bg-red-500",
25
+ } as const satisfies Record<NetworkPolicyPeerType, string>;
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { renderToString } from 'react-dom/server'
3
+ import { PolicyReportCell } from './kyverno-cells'
4
+ import { getPolicyReportScope } from '../resource-utils-kyverno'
5
+
6
+ // Kyverno names each per-resource report after the subject's UID, so the Name
7
+ // column is a bare UUID. Without the subject, the table is a list of
8
+ // unidentifiable rows with counts beside them.
9
+ describe('PolicyReport subject column', () => {
10
+ const report = {
11
+ metadata: { name: '4c1d2f8a-91e3-4b7c-9f11-2a6e0c5d7b83', namespace: 'payments' },
12
+ scope: { kind: 'Deployment', namespace: 'payments', name: 'checkout-api' },
13
+ }
14
+
15
+ it('names the workload the report is about', () => {
16
+ const html = renderToString(<PolicyReportCell resource={report} column="scope" />)
17
+ expect(html).toContain('checkout-api')
18
+ expect(html).toContain('Deployment')
19
+ })
20
+
21
+ it('degrades to a dash rather than an empty cell when there is no subject', () => {
22
+ // Cluster-scoped reports and some producers omit scope entirely.
23
+ expect(getPolicyReportScope({ metadata: { name: 'x' } })).toBe('-')
24
+ const html = renderToString(<PolicyReportCell resource={{ metadata: { name: 'x' } }} column="scope" />)
25
+ expect(html).toContain('-')
26
+ })
27
+ })
@@ -16,6 +16,7 @@ import {
16
16
  getRestoreIncludedNamespaces,
17
17
  getRestoreDuration,
18
18
  getRestoreErrors,
19
+ getRestoreWarnings,
19
20
  getScheduleStatus,
20
21
  getSchedulePaused,
21
22
  getScheduleCronInfo,
@@ -37,6 +38,10 @@ import {
37
38
  // whenever the two differ. Without this the three collapsed partial-failure
38
39
  // phases would be unreachable outside the YAML tab, and the label would stop
39
40
  // being a display choice and start being a loss of information.
41
+ // Foreground colours here carry a light counterpart as well as the dark one.
42
+ // A bare `text-*-400` is tuned for the dark ground and washes out on the light
43
+ // one — the same defect rbac-badges.ts was created to fix, and these cells sit
44
+ // in a table next to badges that already do this correctly.
40
45
  export function VeleroPhaseValue({
41
46
  status,
42
47
  phase,
@@ -84,7 +89,7 @@ export function BackupCell({ resource, column }: { resource: any; column: string
84
89
  case 'expiry': {
85
90
  const exp = getBackupExpiry(resource)
86
91
  const isExpired = exp === 'Expired'
87
- return <span className={clsx('text-sm', isExpired ? 'text-red-400' : 'text-theme-text-secondary')}>{exp}</span>
92
+ return <span className={clsx('text-sm', isExpired ? 'text-red-600 dark:text-red-400' : 'text-theme-text-secondary')}>{exp}</span>
88
93
  }
89
94
  case 'errors': {
90
95
  const errors = getBackupErrors(resource)
@@ -94,9 +99,9 @@ export function BackupCell({ resource, column }: { resource: any; column: string
94
99
  }
95
100
  return (
96
101
  <span className="text-sm">
97
- {errors > 0 && <span className="text-red-400">{errors}E</span>}
102
+ {errors > 0 && <span className="text-red-600 dark:text-red-400">{errors}E</span>}
98
103
  {errors > 0 && warnings > 0 && <span className="text-theme-text-tertiary"> / </span>}
99
- {warnings > 0 && <span className="text-yellow-400">{warnings}W</span>}
104
+ {warnings > 0 && <span className="text-amber-600 dark:text-amber-400">{warnings}W</span>}
100
105
  </span>
101
106
  )
102
107
  }
@@ -128,11 +133,21 @@ export function RestoreCell({ resource, column }: { resource: any; column: strin
128
133
  return <span className="text-sm text-theme-text-secondary">{dur}</span>
129
134
  }
130
135
  case 'errors': {
136
+ // Errors AND warnings, the same shape the Backup column uses. A restore
137
+ // that reported only warnings showed "-" here while carrying a non-zero
138
+ // count on the object, which is the row a reader scans to decide whether
139
+ // the restore is worth opening.
131
140
  const errors = getRestoreErrors(resource)
132
- if (errors === 0) {
141
+ const warnings = getRestoreWarnings(resource)
142
+ if (errors === 0 && warnings === 0) {
133
143
  return <span className="text-sm text-theme-text-tertiary">-</span>
134
144
  }
135
- return <span className="text-sm text-red-400">{errors}</span>
145
+ return (
146
+ <span className="text-sm flex gap-1.5">
147
+ {errors > 0 && <span className="text-red-600 dark:text-red-400">{errors}E</span>}
148
+ {warnings > 0 && <span className="text-amber-600 dark:text-amber-400">{warnings}W</span>}
149
+ </span>
150
+ )
136
151
  }
137
152
  default:
138
153
  return <span className="text-sm text-theme-text-tertiary">-</span>
@@ -157,7 +172,14 @@ export function ScheduleCell({ resource, column }: { resource: any; column: stri
157
172
  </span>
158
173
  {alsoPaused && (
159
174
  <Tooltip content="Also paused — this schedule will not run until it is resumed">
160
- <Pause className="w-3 h-3 shrink-0 text-amber-600 dark:text-amber-400" />
175
+ <>
176
+ <Pause className="w-3 h-3 shrink-0 text-amber-600 dark:text-amber-400" aria-hidden="true" />
177
+ {/* The badge beside this says "Rejected", so without a label the
178
+ row reads as a schedule that is merely broken rather than one
179
+ that is also switched off — and the tooltip carrying that
180
+ second fact needs a pointer to reach. */}
181
+ <span className="sr-only">Also paused — this schedule will not run until it is resumed</span>
182
+ </>
161
183
  </Tooltip>
162
184
  )}
163
185
  </span>
@@ -271,7 +293,7 @@ export function BackupStorageLocationCell({ resource, column }: { resource: any;
271
293
  }
272
294
  case 'default': {
273
295
  const isDefault = getBSLDefault(resource)
274
- return <span className={clsx('text-sm', isDefault ? 'text-blue-400' : 'text-theme-text-tertiary')}>{isDefault ? 'Yes' : '-'}</span>
296
+ return <span className={clsx('text-sm', isDefault ? 'text-blue-600 dark:text-blue-400' : 'text-theme-text-tertiary')}>{isDefault ? 'Yes' : '-'}</span>
275
297
  }
276
298
  case 'lastValidation': {
277
299
  const lastVal = getBSLLastValidation(resource)