@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.
- package/package.json +4 -4
- package/src/components/gitops/GitOpsStatusBadge.tsx +40 -0
- package/src/components/gitops/GitOpsTableView.tsx +22 -2
- package/src/components/gitops/ReconcilingIndicator.test.tsx +31 -0
- package/src/components/gitops/detail-helpers.ts +4 -1
- package/src/components/issues/severity.ts +1 -0
- package/src/components/resources/ResourcesView.empty-note.test.ts +41 -0
- package/src/components/resources/ResourcesView.tsx +412 -5
- package/src/components/resources/index.ts +27 -0
- package/src/components/resources/kyverno-legacy-action.test.ts +98 -0
- package/src/components/resources/renderers/AlertRenderer.tsx +4 -1
- package/src/components/resources/renderers/AnalysisRunRenderer.test.tsx +241 -0
- package/src/components/resources/renderers/AnalysisRunRenderer.tsx +166 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +31 -2
- package/src/components/resources/renderers/CNPGClusterRenderer.test.tsx +33 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +71 -4
- package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +243 -0
- package/src/components/resources/renderers/CNPGObjectStoreRenderer.test.tsx +99 -0
- package/src/components/resources/renderers/CNPGObjectStoreRenderer.tsx +235 -0
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +11 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +23 -1
- package/src/components/resources/renderers/CNPGShared.tsx +27 -0
- package/src/components/resources/renderers/CalicoHostEndpointRenderer.tsx +71 -0
- package/src/components/resources/renderers/CalicoIPPoolRenderer.tsx +32 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.test.tsx +205 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.tsx +537 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.test.tsx +223 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.tsx +670 -0
- package/src/components/resources/renderers/CalicoTierRenderer.tsx +19 -0
- package/src/components/resources/renderers/CompositeRenderer.test.tsx +81 -0
- package/src/components/resources/renderers/CompositeRenderer.tsx +67 -7
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +4 -1
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +4 -1
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +4 -1
- package/src/components/resources/renderers/KustomizationRenderer.tsx +4 -1
- package/src/components/resources/renderers/KyvernoCELPolicyRenderers.tsx +13 -6
- package/src/components/resources/renderers/KyvernoExceptionRenderers.tsx +3 -2
- package/src/components/resources/renderers/KyvernoGlobalContextRenderer.test.ts +31 -0
- package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +124 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +28 -2
- package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +303 -0
- package/src/components/resources/renderers/LookupFailureNote.test.tsx +60 -0
- package/src/components/resources/renderers/LookupFailureNote.tsx +42 -0
- package/src/components/resources/renderers/NetworkPolicyDiagram.tsx +27 -26
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -3
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +4 -1
- package/src/components/resources/renderers/PodRenderer.tsx +13 -0
- package/src/components/resources/renderers/PolicyCoverageSection.test.tsx +348 -0
- package/src/components/resources/renderers/PolicyCoverageSection.tsx +939 -0
- package/src/components/resources/renderers/PolicySection.test.tsx +159 -0
- package/src/components/resources/renderers/PolicySection.tsx +197 -0
- package/src/components/resources/renderers/RolloutRenderer.test.tsx +491 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +391 -92
- package/src/components/resources/renderers/VeleroBSLRenderer.test.tsx +374 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +179 -3
- package/src/components/resources/renderers/VeleroBackupRenderer.test.tsx +221 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +119 -6
- package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.test.tsx +69 -0
- package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +96 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.test.tsx +55 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +22 -4
- package/src/components/resources/renderers/VeleroRunMessages.test.tsx +152 -0
- package/src/components/resources/renderers/VeleroRunMessages.tsx +139 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.test.tsx +88 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +16 -6
- package/src/components/resources/renderers/WorkloadRenderer.tsx +15 -1
- package/src/components/resources/renderers/calico-cells.tsx +177 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +100 -0
- package/src/components/resources/renderers/index.ts +16 -0
- package/src/components/resources/renderers/kyverno-cells.tsx +90 -0
- package/src/components/resources/renderers/network-policy-peer-styles.ts +25 -0
- package/src/components/resources/renderers/policyreport-subject.test.tsx +27 -0
- package/src/components/resources/renderers/velero-cells.tsx +29 -7
- package/src/components/resources/renderers/velero-dead-ends.test.tsx +142 -0
- package/src/components/resources/resource-utils-calico.test.ts +215 -0
- package/src/components/resources/resource-utils-calico.ts +261 -0
- package/src/components/resources/resource-utils-cnpg-declarative.test.ts +104 -0
- package/src/components/resources/resource-utils-cnpg-objectstore.test.ts +122 -0
- package/src/components/resources/resource-utils-cnpg.test.ts +117 -0
- package/src/components/resources/resource-utils-cnpg.ts +324 -3
- package/src/components/resources/resource-utils-crossplane.test.ts +68 -0
- package/src/components/resources/resource-utils-kyverno-modern.ts +24 -0
- package/src/components/resources/resource-utils-kyverno-queue.test.ts +164 -0
- package/src/components/resources/resource-utils-kyverno-queue.ts +198 -0
- package/src/components/resources/resource-utils-kyverno.ts +43 -7
- package/src/components/resources/resource-utils-velero.test.ts +26 -0
- package/src/components/resources/resource-utils-velero.ts +68 -0
- package/src/components/resources/resource-utils.ts +98 -0
- package/src/components/shared/ResourceActionsBar.tsx +101 -16
- package/src/components/shared/ResourceRendererDispatch.test.tsx +379 -3
- package/src/components/shared/ResourceRendererDispatch.tsx +199 -22
- package/src/components/shared/index.ts +1 -1
- package/src/components/shared/revision-role-badges.test.ts +120 -0
- package/src/components/topology/K8sResourceNode.test.ts +10 -0
- package/src/components/topology/K8sResourceNode.tsx +22 -0
- package/src/components/topology/TopologyFilterSidebar.test.tsx +31 -0
- package/src/components/topology/TopologyFilterSidebar.tsx +5 -0
- package/src/components/topology/TopologyGraph.tsx +6 -6
- package/src/components/topology/TopologySearch.tsx +7 -0
- package/src/components/topology/layout.ts +3 -0
- package/src/components/trace/ReachabilityView.tsx +1 -0
- package/src/components/trace/TracePanel.tsx +12 -2
- package/src/components/trace/reachFixtures.ts +26 -1
- package/src/components/trace/reachGraphModel.test.ts +20 -0
- package/src/components/trace/reachGraphModel.ts +8 -1
- package/src/components/trace/reachOrigins.test.ts +31 -0
- package/src/components/trace/reachOrigins.ts +11 -1
- package/src/components/ui/Badge.tsx +10 -0
- package/src/components/ui/ScheduleValue.test.tsx +80 -0
- package/src/components/ui/ScheduleValue.tsx +85 -0
- package/src/components/ui/drawer-components.test.tsx +17 -1
- package/src/components/ui/drawer-components.tsx +9 -2
- package/src/components/ui/index.ts +2 -0
- package/src/components/workload/WorkloadView.tsx +3 -0
- package/src/types/core.ts +29 -0
- package/src/types/gitops-flux-status.test.ts +163 -0
- package/src/types/gitops.ts +80 -24
- package/src/types/index.ts +1 -0
- package/src/types/policy.ts +193 -0
- package/src/utils/navigation.test.ts +5 -0
- package/src/utils/navigation.ts +5 -0
- package/src/utils/resource-icons.test.ts +49 -2
- 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-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|