@skyhook-io/k8s-ui 1.12.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/issues/severity.ts +1 -0
- package/src/components/resources/ResourcesView.empty-note.test.ts +41 -0
- package/src/components/resources/ResourcesView.tsx +410 -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/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 +47 -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/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/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/PolicyCoverageSection.test.tsx +348 -0
- package/src/components/resources/renderers/PolicyCoverageSection.tsx +939 -0
- package/src/components/resources/renderers/PolicySection.test.tsx +59 -2
- package/src/components/resources/renderers/PolicySection.tsx +32 -5
- 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/calico-cells.tsx +177 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +100 -0
- package/src/components/resources/renderers/index.ts +16 -1
- package/src/components/resources/renderers/kyverno-cells.tsx +81 -0
- package/src/components/resources/renderers/network-policy-peer-styles.ts +25 -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 +54 -0
- package/src/components/resources/resource-utils-cnpg.ts +264 -3
- 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/policy.ts +153 -2
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyhook-io/k8s-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/skyhook-io/radar",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"html-to-image": "^1.11.0",
|
|
72
72
|
"monaco-yaml": "5.5.1",
|
|
73
73
|
"react-virtuoso": "^4.18.11",
|
|
74
|
-
"shiki": "^4.2
|
|
74
|
+
"shiki": "^4.4.2"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@xterm/addon-fit": ">=0.10.0",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"yaml": ">=2.0.0"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@types/node": "^26.
|
|
91
|
+
"@types/node": "^26.2.0",
|
|
92
92
|
"@types/react": "^19.2.18",
|
|
93
93
|
"@types/react-dom": "^19.2.3",
|
|
94
94
|
"@xterm/addon-fit": "^0.11.0",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"clsx": "^2.1.1",
|
|
99
99
|
"diff": "^9.0.0",
|
|
100
100
|
"elkjs": "^0.11.1",
|
|
101
|
-
"lucide-react": "^1.
|
|
101
|
+
"lucide-react": "^1.30.0",
|
|
102
102
|
"monaco-editor": "0.55.1",
|
|
103
103
|
"react": "^19.2.8",
|
|
104
104
|
"react-dom": "^19.2.8",
|
|
@@ -85,6 +85,7 @@ const CATEGORY_LABEL: Record<string, string> = {
|
|
|
85
85
|
volume_access_mode_conflict: 'Volume access conflict',
|
|
86
86
|
backup_failed: 'Backup failed',
|
|
87
87
|
backup_target_unavailable: 'Backup target unavailable',
|
|
88
|
+
backup_stalled: 'Backup not progressing',
|
|
88
89
|
job_failed: 'Job failed',
|
|
89
90
|
cronjob_failed: 'CronJob failed',
|
|
90
91
|
rollout_stalled: 'Rollout stalled',
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { emptyKindNote } from './ResourcesView'
|
|
3
|
+
|
|
4
|
+
describe('emptyKindNote', () => {
|
|
5
|
+
it('explains that no UpdateRequest is the resting state, and that it looks the same as a rule that never ran', () => {
|
|
6
|
+
const note = emptyKindNote('UpdateRequest', 'kyverno.io')
|
|
7
|
+
expect(note).toContain('deleted seconds after')
|
|
8
|
+
expect(note).toContain('never ran')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('says an absent EphemeralReport means the scan finished, not that nothing was checked', () => {
|
|
12
|
+
expect(emptyKindNote('EphemeralReport', 'reports.kyverno.io')).toContain('scan has finished')
|
|
13
|
+
expect(emptyKindNote('ClusterEphemeralReport', 'reports.kyverno.io')).toContain('scan has finished')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('is case-insensitive on the kind, which arrives from a URL as often as from discovery', () => {
|
|
17
|
+
expect(emptyKindNote('updaterequest', 'kyverno.io')).toBeDefined()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// Same plural, different operator: Kyverno's group is what makes the note true.
|
|
21
|
+
it('says nothing about a kind of the same name from another group', () => {
|
|
22
|
+
expect(emptyKindNote('UpdateRequest', 'example.com')).toBeUndefined()
|
|
23
|
+
expect(emptyKindNote('EphemeralReport', '')).toBeUndefined()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('says nothing about kinds with no such story', () => {
|
|
27
|
+
expect(emptyKindNote('Pod', '')).toBeUndefined()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// The table can be empty because the reader emptied it. Telling them none is
|
|
31
|
+
// normal then answers a question they did not ask, with their term still in
|
|
32
|
+
// the search box.
|
|
33
|
+
it('stays silent when the kind has rows and a filter is what emptied the table', () => {
|
|
34
|
+
expect(emptyKindNote('UpdateRequest', 'kyverno.io', 3)).toBeUndefined()
|
|
35
|
+
expect(emptyKindNote('EphemeralReport', 'reports.kyverno.io', 192)).toBeUndefined()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('still speaks when the kind is genuinely empty', () => {
|
|
39
|
+
expect(emptyKindNote('UpdateRequest', 'kyverno.io', 0)).toBeDefined()
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -79,6 +79,8 @@ import {
|
|
|
79
79
|
getPVCCapacity,
|
|
80
80
|
getPVCAccessModes,
|
|
81
81
|
getRolloutStatus,
|
|
82
|
+
getAnalysisRunStatus,
|
|
83
|
+
summarizeAnalysisMetrics,
|
|
82
84
|
getRolloutStrategy,
|
|
83
85
|
getRolloutReady,
|
|
84
86
|
getRolloutStep,
|
|
@@ -152,14 +154,17 @@ import { ScaledObjectCell, ScaledJobCell, TriggerAuthenticationCell, ClusterTrig
|
|
|
152
154
|
import { ResourceClaimCell, ResourceClaimTemplateCell, DeviceClassCell, ResourceSliceCell } from './renderers/dra-cells'
|
|
153
155
|
import { NvidiaClusterPolicyCell, NvidiaDriverCell } from './renderers/nvidia-cells'
|
|
154
156
|
import { ServiceMonitorCell, PrometheusRuleCell, PodMonitorCell } from './renderers/prometheus-cells'
|
|
155
|
-
import { PolicyReportCell, ClusterPolicyReportCell, KyvernoPolicyCell, ClusterPolicyCell
|
|
157
|
+
import { PolicyReportCell, ClusterPolicyReportCell, KyvernoPolicyCell, ClusterPolicyCell,
|
|
158
|
+
KyvernoUpdateRequestCell,
|
|
159
|
+
KyvernoEphemeralReportCell,
|
|
160
|
+
} from './renderers/kyverno-cells'
|
|
156
161
|
import { KyvernoModernPolicyCell, KyvernoPolicyExceptionCell, KyvernoCleanupPolicyCell } from './renderers/kyverno-modern-cells'
|
|
157
162
|
import { KYVERNO_MODERN_PLURALS, isModernKyvernoPolicy } from './resource-utils-kyverno-modern'
|
|
158
163
|
import { isAnyKyvernoPolicyException } from './resource-utils-kyverno-exceptions'
|
|
159
164
|
import { ExternalSecretCell, ClusterExternalSecretCell, SecretStoreCell, ClusterSecretStoreCell } from './renderers/eso-cells'
|
|
160
165
|
import { BackupCell, RestoreCell, ScheduleCell, BackupStorageLocationCell, VolumeSnapshotLocationCell, BackupRepositoryCell } from './renderers/velero-cells'
|
|
161
166
|
import { isVeleroResource } from './resource-utils-velero'
|
|
162
|
-
import { CNPGClusterCell, CNPGBackupCell, CNPGScheduledBackupCell, CNPGPoolerCell } from './renderers/cnpg-cells'
|
|
167
|
+
import { CNPGClusterCell, CNPGBackupCell, CNPGScheduledBackupCell, CNPGPoolerCell, CNPGObjectStoreCell, CNPGDeclarativeCell, CNPGImageCatalogCell } from './renderers/cnpg-cells'
|
|
163
168
|
import { isApiGroup, CNPG_GROUP } from './resource-utils-cnpg'
|
|
164
169
|
import { ManagedResourceCell, CompositeResourceCell, CrossplaneProviderCell, CrossplaneProviderConfigCell, CompositionCell, XRDCell } from './renderers/crossplane-cells'
|
|
165
170
|
import { isManagedResource, isComposite } from './resource-utils-crossplane'
|
|
@@ -171,6 +176,8 @@ import { CAPIClusterCell, CAPIMachineCell, CAPIMachineDeploymentCell, CAPIMachin
|
|
|
171
176
|
import { AWSManagedControlPlaneCell, AWSManagedMachinePoolCell, AWSMachineCell, AWSMachineTemplateCell, AWSManagedClusterCell } from './renderers/aws-capi-cells'
|
|
172
177
|
import { GCPManagedControlPlaneCell, GCPManagedMachinePoolCell, GCPMachineCell, GCPMachineTemplateCell, GCPManagedClusterCell } from './renderers/gcp-capi-cells'
|
|
173
178
|
import { AzureManagedControlPlaneCell, AzureManagedMachinePoolCell, AzureMachineCell, AzureMachineTemplateCell, AzureManagedClusterCell } from './renderers/azure-capi-cells'
|
|
179
|
+
import { CalicoInfraCell, CalicoPolicyCell } from './renderers/calico-cells'
|
|
180
|
+
import { isCalicoPolicyResource, isCoreNetworkPolicyKind } from './resource-utils-calico'
|
|
174
181
|
import { useRegisterShortcut, useRegisterShortcuts } from '../../hooks/useKeyboardShortcuts'
|
|
175
182
|
import { ResourcesSidebar } from './ResourcesSidebar'
|
|
176
183
|
import type { SelectedKindInfo } from './ResourcesSidebar'
|
|
@@ -274,10 +281,14 @@ function buildCustomColumn(d: CustomColumnDef): ExtraColumn {
|
|
|
274
281
|
}
|
|
275
282
|
|
|
276
283
|
// Tailwind width class → pixel minimum mapping for CSS Grid column sizing
|
|
284
|
+
// A width class missing from this map falls back to 80px, and nothing says so —
|
|
285
|
+
// the column just renders narrow enough to truncate its header. Keep the whole
|
|
286
|
+
// Tailwind step range present rather than only the sizes in use today.
|
|
277
287
|
const TAILWIND_WIDTH_TO_PX: Record<string, number> = {
|
|
278
288
|
'w-12': 48, 'w-14': 56, 'w-16': 64, 'w-20': 80, 'w-24': 96,
|
|
279
289
|
'w-28': 112, 'w-32': 128, 'w-36': 144, 'w-40': 160, 'w-44': 176,
|
|
280
|
-
'w-48': 192, 'w-56': 224, 'w-64': 256,
|
|
290
|
+
'w-48': 192, 'w-52': 208, 'w-56': 224, 'w-60': 240, 'w-64': 256,
|
|
291
|
+
'w-72': 288, 'w-80': 320, 'w-96': 384,
|
|
281
292
|
}
|
|
282
293
|
|
|
283
294
|
const COMPARE_COLUMN_WIDTH = 36
|
|
@@ -296,6 +307,30 @@ function getColumnMinWidth(col: Column): number {
|
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
// Default columns for unknown resource types (CRDs)
|
|
310
|
+
/**
|
|
311
|
+
* Why a list of this kind being empty is not the answer it looks like.
|
|
312
|
+
*
|
|
313
|
+
* Kyverno's two working records are garbage-collected within seconds of
|
|
314
|
+
* finishing, so "none" is the resting state of a healthy cluster — and for
|
|
315
|
+
* UpdateRequest it is ALSO what you see when a generate rule never fired at
|
|
316
|
+
* all. Those are opposite conclusions and a bare "No X found" renders them
|
|
317
|
+
* identically.
|
|
318
|
+
*/
|
|
319
|
+
export function emptyKindNote(kind: string, group?: string, unfilteredTotal = 0): string | undefined {
|
|
320
|
+
// A search or column filter emptying the table is the reader's own doing.
|
|
321
|
+
// Explaining that none is the resting state then answers a question they did
|
|
322
|
+
// not ask, while their own term is still in the box.
|
|
323
|
+
if (unfilteredTotal > 0) return undefined
|
|
324
|
+
const g = group ?? ''
|
|
325
|
+
if (kind.toLowerCase() === 'updaterequest' && g === 'kyverno.io') {
|
|
326
|
+
return 'These are deleted seconds after the work completes, so none is the normal resting state. It also looks like this if a generate or mutate-existing rule never ran — check the policy itself to tell the two apart.'
|
|
327
|
+
}
|
|
328
|
+
if ((kind.toLowerCase() === 'ephemeralreport' || kind.toLowerCase() === 'clusterephemeralreport') && g === 'reports.kyverno.io') {
|
|
329
|
+
return 'These exist only between a background scan and the PolicyReport it becomes, so none means the scan has finished rather than that nothing was checked.'
|
|
330
|
+
}
|
|
331
|
+
return undefined
|
|
332
|
+
}
|
|
333
|
+
|
|
299
334
|
const DEFAULT_COLUMNS: Column[] = [
|
|
300
335
|
{ key: 'name', label: 'Name' },
|
|
301
336
|
{ key: 'namespace', label: 'Namespace', width: 'w-48' },
|
|
@@ -464,6 +499,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
|
|
|
464
499
|
{ key: 'images', label: 'Images', width: 'w-48', hideOnMobile: true },
|
|
465
500
|
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
466
501
|
],
|
|
502
|
+
analysisruns: [
|
|
503
|
+
{ key: 'name', label: 'Name' },
|
|
504
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-48' },
|
|
505
|
+
{ key: 'status', label: 'Phase', width: 'w-28' },
|
|
506
|
+
{ key: 'trigger', label: 'Trigger', width: 'w-32', tooltip: 'Which part of the Rollout started this run' },
|
|
507
|
+
{ key: 'metrics', label: 'Metrics', width: 'w-40', hideOnMobile: true, tooltip: 'Per-metric verdicts' },
|
|
508
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
509
|
+
],
|
|
467
510
|
workflows: [
|
|
468
511
|
{ key: 'name', label: 'Name' },
|
|
469
512
|
{ key: 'namespace', label: 'Namespace', width: 'w-48' },
|
|
@@ -913,6 +956,88 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
|
|
|
913
956
|
{ key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
|
|
914
957
|
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
915
958
|
],
|
|
959
|
+
caliconetworkpolicies: [
|
|
960
|
+
{ key: 'name', label: 'Name' },
|
|
961
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
962
|
+
{ key: 'selector', label: 'Selector', width: 'w-48' },
|
|
963
|
+
{ key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
|
|
964
|
+
{ key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
|
|
965
|
+
{ key: 'tier', label: 'Tier', width: 'w-28' },
|
|
966
|
+
{ key: 'order', label: 'Order', width: 'w-24' },
|
|
967
|
+
{ key: 'types', label: 'Types', width: 'w-28' },
|
|
968
|
+
{ key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
|
|
969
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
970
|
+
],
|
|
971
|
+
calicoglobalnetworkpolicies: [
|
|
972
|
+
{ key: 'name', label: 'Name' },
|
|
973
|
+
{ key: 'selector', label: 'Selector', width: 'w-48' },
|
|
974
|
+
{ key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
|
|
975
|
+
{ key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
|
|
976
|
+
{ key: 'tier', label: 'Tier', width: 'w-28' },
|
|
977
|
+
{ key: 'order', label: 'Order', width: 'w-24' },
|
|
978
|
+
{ key: 'types', label: 'Types', width: 'w-28' },
|
|
979
|
+
{ key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
|
|
980
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
981
|
+
],
|
|
982
|
+
calicostagednetworkpolicies: [
|
|
983
|
+
{ key: 'name', label: 'Name' },
|
|
984
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
985
|
+
{ key: 'selector', label: 'Selector', width: 'w-48' },
|
|
986
|
+
{ key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
|
|
987
|
+
{ key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
|
|
988
|
+
{ key: 'tier', label: 'Tier', width: 'w-28' },
|
|
989
|
+
{ key: 'order', label: 'Order', width: 'w-24' },
|
|
990
|
+
{ key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
|
|
991
|
+
{ key: 'types', label: 'Types', width: 'w-28' },
|
|
992
|
+
{ key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
|
|
993
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
994
|
+
],
|
|
995
|
+
calicostagedglobalnetworkpolicies: [
|
|
996
|
+
{ key: 'name', label: 'Name' },
|
|
997
|
+
{ key: 'selector', label: 'Selector', width: 'w-48' },
|
|
998
|
+
{ key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
|
|
999
|
+
{ key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
|
|
1000
|
+
{ key: 'tier', label: 'Tier', width: 'w-28' },
|
|
1001
|
+
{ key: 'order', label: 'Order', width: 'w-24' },
|
|
1002
|
+
{ key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
|
|
1003
|
+
{ key: 'types', label: 'Types', width: 'w-28' },
|
|
1004
|
+
{ key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
|
|
1005
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1006
|
+
],
|
|
1007
|
+
calicoippools: [
|
|
1008
|
+
{ key: 'name', label: 'Name' },
|
|
1009
|
+
{ key: 'cidr', label: 'CIDR', width: 'w-44' },
|
|
1010
|
+
{ key: 'blockSize', label: 'Block Size', width: 'w-28' },
|
|
1011
|
+
{ key: 'encapsulation', label: 'Encapsulation', width: 'w-36' },
|
|
1012
|
+
{ key: 'natOutgoing', label: 'NAT Outgoing', width: 'w-32' },
|
|
1013
|
+
{ key: 'disabled', label: 'Disabled', width: 'w-28' },
|
|
1014
|
+
{ key: 'allowedUses', label: 'Allowed Uses', width: 'w-40', defaultVisible: false },
|
|
1015
|
+
{ key: 'nodeSelector', label: 'Node Selector', width: 'w-48', defaultVisible: false },
|
|
1016
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1017
|
+
],
|
|
1018
|
+
calicohostendpoints: [
|
|
1019
|
+
{ key: 'name', label: 'Name' },
|
|
1020
|
+
{ key: 'node', label: 'Node', width: 'w-48' },
|
|
1021
|
+
{ key: 'interfaceName', label: 'Interface', width: 'w-32' },
|
|
1022
|
+
{ key: 'expectedIPs', label: 'Expected IPs', width: 'w-48' },
|
|
1023
|
+
{ key: 'profiles', label: 'Profiles', width: 'w-48', defaultVisible: false },
|
|
1024
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1025
|
+
],
|
|
1026
|
+
calicotiers: [
|
|
1027
|
+
{ key: 'name', label: 'Name' },
|
|
1028
|
+
{ key: 'order', label: 'Order', width: 'w-24' },
|
|
1029
|
+
{ key: 'defaultAction', label: 'Default Action', width: 'w-36' },
|
|
1030
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1031
|
+
],
|
|
1032
|
+
calicostagedkubernetesnetworkpolicies: [
|
|
1033
|
+
{ key: 'name', label: 'Name' },
|
|
1034
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1035
|
+
{ key: 'selector', label: 'Pod Selector', width: 'w-48' },
|
|
1036
|
+
{ key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
|
|
1037
|
+
{ key: 'types', label: 'Types', width: 'w-28' },
|
|
1038
|
+
{ key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
|
|
1039
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1040
|
+
],
|
|
916
1041
|
poddisruptionbudgets: [
|
|
917
1042
|
{ key: 'name', label: 'Name' },
|
|
918
1043
|
{ key: 'namespace', label: 'Namespace', width: 'w-48' },
|
|
@@ -1305,6 +1430,92 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
|
|
|
1305
1430
|
// than an absent one — the reader can't tell which meaning they're getting.
|
|
1306
1431
|
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1307
1432
|
],
|
|
1433
|
+
// `databases`, `publications` and `subscriptions` are crowded plurals — other
|
|
1434
|
+
// database operators ship all three, and Knative owns the unqualified
|
|
1435
|
+
// `subscriptions` set. Keyed under cnpg* and reached only through
|
|
1436
|
+
// GROUP_QUALIFIED_COLUMN_KEYS, so a foreign CRD gets the generic columns
|
|
1437
|
+
// instead of a Cluster column it can never fill.
|
|
1438
|
+
// Kyverno's working records. Both are keyed by things the reader has to see
|
|
1439
|
+
// to know whether anything is wrong: which policy queued the work and what it
|
|
1440
|
+
// was queued against, and which resource a scan is about.
|
|
1441
|
+
updaterequests: [
|
|
1442
|
+
{ key: 'name', label: 'Name' },
|
|
1443
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-32' },
|
|
1444
|
+
{ key: 'status', label: 'State', width: 'w-28' },
|
|
1445
|
+
{ key: 'type', label: 'Type', width: 'w-24' },
|
|
1446
|
+
{ key: 'policy', label: 'Policy', width: 'w-52' },
|
|
1447
|
+
{ key: 'triggers', label: 'Triggered By', width: 'w-56' },
|
|
1448
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1449
|
+
],
|
|
1450
|
+
ephemeralreports: [
|
|
1451
|
+
{ key: 'name', label: 'Name' },
|
|
1452
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-32' },
|
|
1453
|
+
{ key: 'status', label: 'Outcome', width: 'w-28' },
|
|
1454
|
+
{ key: 'subject', label: 'About', width: 'w-56' },
|
|
1455
|
+
{ key: 'source', label: 'Produced By', width: 'w-36' },
|
|
1456
|
+
{ key: 'results', label: 'Results', width: 'w-24' },
|
|
1457
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1458
|
+
],
|
|
1459
|
+
clusterephemeralreports: [
|
|
1460
|
+
{ key: 'name', label: 'Name' },
|
|
1461
|
+
{ key: 'status', label: 'Outcome', width: 'w-28' },
|
|
1462
|
+
{ key: 'subject', label: 'About', width: 'w-56' },
|
|
1463
|
+
{ key: 'source', label: 'Produced By', width: 'w-36' },
|
|
1464
|
+
{ key: 'results', label: 'Results', width: 'w-24' },
|
|
1465
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1466
|
+
],
|
|
1467
|
+
cnpgdatabases: [
|
|
1468
|
+
{ key: 'name', label: 'Name' },
|
|
1469
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1470
|
+
{ key: 'status', label: 'Applied', width: 'w-32' },
|
|
1471
|
+
{ key: 'cluster', label: 'Cluster', width: 'w-36' },
|
|
1472
|
+
{ key: 'target', label: 'Database', width: 'w-40' },
|
|
1473
|
+
{ key: 'message', label: 'Message', width: 'w-64' },
|
|
1474
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1475
|
+
],
|
|
1476
|
+
cnpgpublications: [
|
|
1477
|
+
{ key: 'name', label: 'Name' },
|
|
1478
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1479
|
+
{ key: 'status', label: 'Applied', width: 'w-32' },
|
|
1480
|
+
{ key: 'cluster', label: 'Cluster', width: 'w-36' },
|
|
1481
|
+
{ key: 'dbname', label: 'In Database', width: 'w-40' },
|
|
1482
|
+
{ key: 'message', label: 'Message', width: 'w-64' },
|
|
1483
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1484
|
+
],
|
|
1485
|
+
cnpgsubscriptions: [
|
|
1486
|
+
{ key: 'name', label: 'Name' },
|
|
1487
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1488
|
+
{ key: 'status', label: 'Applied', width: 'w-32' },
|
|
1489
|
+
{ key: 'cluster', label: 'Cluster', width: 'w-36' },
|
|
1490
|
+
{ key: 'dbname', label: 'In Database', width: 'w-40' },
|
|
1491
|
+
{ key: 'message', label: 'Message', width: 'w-64' },
|
|
1492
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1493
|
+
],
|
|
1494
|
+
cnpgimagecatalogs: [
|
|
1495
|
+
{ key: 'name', label: 'Name' },
|
|
1496
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1497
|
+
{ key: 'majors', label: 'Major Versions', width: 'w-40' },
|
|
1498
|
+
{ key: 'images', label: 'Images', width: 'w-24' },
|
|
1499
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1500
|
+
],
|
|
1501
|
+
cnpgclusterimagecatalogs: [
|
|
1502
|
+
{ key: 'name', label: 'Name' },
|
|
1503
|
+
{ key: 'majors', label: 'Major Versions', width: 'w-40' },
|
|
1504
|
+
{ key: 'images', label: 'Images', width: 'w-24' },
|
|
1505
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1506
|
+
],
|
|
1507
|
+
barmanobjectstores: [
|
|
1508
|
+
{ key: 'name', label: 'Name' },
|
|
1509
|
+
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1510
|
+
{ key: 'status', label: 'Status', width: 'w-40' },
|
|
1511
|
+
// Sized rather than flexible: every sibling table gives its long value a
|
|
1512
|
+
// fixed width, and leaving two columns unsized let Name absorb the row at
|
|
1513
|
+
// 1920px — truncating the destination and pushing Retention off-screen.
|
|
1514
|
+
{ key: 'destination', label: 'Destination', width: 'w-64' },
|
|
1515
|
+
{ key: 'servers', label: 'Servers', width: 'w-32' },
|
|
1516
|
+
{ key: 'retention', label: 'Retention', width: 'w-28' },
|
|
1517
|
+
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1518
|
+
],
|
|
1308
1519
|
cnpgbackups: [
|
|
1309
1520
|
{ key: 'name', label: 'Name' },
|
|
1310
1521
|
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
@@ -1499,7 +1710,10 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
|
|
|
1499
1710
|
{ key: 'address', label: 'Address', width: 'w-56' },
|
|
1500
1711
|
{ key: 'age', label: 'Age', width: 'w-24' },
|
|
1501
1712
|
],
|
|
1502
|
-
|
|
1713
|
+
// Keyed under knative*, reached only through GROUP_QUALIFIED_COLUMN_KEYS, so a
|
|
1714
|
+
// third operator's `subscriptions` gets the generic columns instead of Channel
|
|
1715
|
+
// and Subscriber it can never fill.
|
|
1716
|
+
knativesubscriptions: [
|
|
1503
1717
|
{ key: 'name', label: 'Name' },
|
|
1504
1718
|
{ key: 'namespace', label: 'Namespace', width: 'w-36' },
|
|
1505
1719
|
{ key: 'status', label: 'Status', width: 'w-28' },
|
|
@@ -1869,6 +2083,16 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
|
|
|
1869
2083
|
|
|
1870
2084
|
// Map (plural, group) → KNOWN_COLUMNS key for kinds that collide with core K8s
|
|
1871
2085
|
const GROUP_QUALIFIED_COLUMN_KEYS: Record<string, Record<string, string>> = {
|
|
2086
|
+
networkpolicy: {
|
|
2087
|
+
'networking.k8s.io': 'networkpolicies',
|
|
2088
|
+
'projectcalico.org': 'caliconetworkpolicies',
|
|
2089
|
+
'crd.projectcalico.org': 'caliconetworkpolicies',
|
|
2090
|
+
},
|
|
2091
|
+
networkpolicies: {
|
|
2092
|
+
'networking.k8s.io': 'networkpolicies',
|
|
2093
|
+
'projectcalico.org': 'caliconetworkpolicies',
|
|
2094
|
+
'crd.projectcalico.org': 'caliconetworkpolicies',
|
|
2095
|
+
},
|
|
1872
2096
|
clusters: { 'postgresql.cnpg.io': 'cnpgclusters', 'cluster.x-k8s.io': 'capiclusters' },
|
|
1873
2097
|
// Velero owns the unqualified `backups` column set; CNPG Backups carry a
|
|
1874
2098
|
// completely different shape (cluster + method, no storage location/expiry).
|
|
@@ -1882,8 +2106,81 @@ const GROUP_QUALIFIED_COLUMN_KEYS: Record<string, Record<string, string>> = {
|
|
|
1882
2106
|
certificates: { 'networking.internal.knative.dev': 'knativecertificates' },
|
|
1883
2107
|
restores: { 'velero.io': 'velerorestores' },
|
|
1884
2108
|
schedules: { 'velero.io': 'veleroschedules' },
|
|
2109
|
+
// CNPG's declarative objects. Knative owns the unqualified `subscriptions`
|
|
2110
|
+
// set, and `databases` / `publications` are shipped by several database
|
|
2111
|
+
// operators — none of which have a CNPG Cluster to point at.
|
|
2112
|
+
databases: { 'postgresql.cnpg.io': 'cnpgdatabases' },
|
|
2113
|
+
publications: { 'postgresql.cnpg.io': 'cnpgpublications' },
|
|
2114
|
+
subscriptions: {
|
|
2115
|
+
'postgresql.cnpg.io': 'cnpgsubscriptions',
|
|
2116
|
+
'messaging.knative.dev': 'knativesubscriptions',
|
|
2117
|
+
},
|
|
2118
|
+
imagecatalogs: { 'postgresql.cnpg.io': 'cnpgimagecatalogs' },
|
|
2119
|
+
clusterimagecatalogs: { 'postgresql.cnpg.io': 'cnpgclusterimagecatalogs' },
|
|
2120
|
+
objectstores: { 'barmancloud.cnpg.io': 'barmanobjectstores' },
|
|
2121
|
+
ippools: {
|
|
2122
|
+
'projectcalico.org': 'calicoippools',
|
|
2123
|
+
'crd.projectcalico.org': 'calicoippools',
|
|
2124
|
+
},
|
|
2125
|
+
hostendpoints: {
|
|
2126
|
+
'projectcalico.org': 'calicohostendpoints',
|
|
2127
|
+
'crd.projectcalico.org': 'calicohostendpoints',
|
|
2128
|
+
},
|
|
2129
|
+
tiers: {
|
|
2130
|
+
'projectcalico.org': 'calicotiers',
|
|
2131
|
+
'crd.projectcalico.org': 'calicotiers',
|
|
2132
|
+
},
|
|
2133
|
+
globalnetworkpolicies: {
|
|
2134
|
+
'projectcalico.org': 'calicoglobalnetworkpolicies',
|
|
2135
|
+
'crd.projectcalico.org': 'calicoglobalnetworkpolicies',
|
|
2136
|
+
},
|
|
2137
|
+
globalnetworkpolicy: {
|
|
2138
|
+
'projectcalico.org': 'calicoglobalnetworkpolicies',
|
|
2139
|
+
'crd.projectcalico.org': 'calicoglobalnetworkpolicies',
|
|
2140
|
+
},
|
|
2141
|
+
stagednetworkpolicies: {
|
|
2142
|
+
'projectcalico.org': 'calicostagednetworkpolicies',
|
|
2143
|
+
'crd.projectcalico.org': 'calicostagednetworkpolicies',
|
|
2144
|
+
},
|
|
2145
|
+
stagednetworkpolicy: {
|
|
2146
|
+
'projectcalico.org': 'calicostagednetworkpolicies',
|
|
2147
|
+
'crd.projectcalico.org': 'calicostagednetworkpolicies',
|
|
2148
|
+
},
|
|
2149
|
+
stagedglobalnetworkpolicies: {
|
|
2150
|
+
'projectcalico.org': 'calicostagedglobalnetworkpolicies',
|
|
2151
|
+
'crd.projectcalico.org': 'calicostagedglobalnetworkpolicies',
|
|
2152
|
+
},
|
|
2153
|
+
stagedglobalnetworkpolicy: {
|
|
2154
|
+
'projectcalico.org': 'calicostagedglobalnetworkpolicies',
|
|
2155
|
+
'crd.projectcalico.org': 'calicostagedglobalnetworkpolicies',
|
|
2156
|
+
},
|
|
2157
|
+
stagedkubernetesnetworkpolicies: {
|
|
2158
|
+
'projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
|
|
2159
|
+
'crd.projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
|
|
2160
|
+
},
|
|
2161
|
+
stagedkubernetesnetworkpolicy: {
|
|
2162
|
+
'projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
|
|
2163
|
+
'crd.projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
|
|
2164
|
+
},
|
|
1885
2165
|
}
|
|
1886
2166
|
|
|
2167
|
+
const GROUP_QUALIFIED_ONLY_COLUMN_KEYS = new Set([
|
|
2168
|
+
'networkpolicy', 'networkpolicies',
|
|
2169
|
+
// Other projects serve their own IPPool CRD, so only a Calico group earns the
|
|
2170
|
+
// Calico columns.
|
|
2171
|
+
'ippool', 'ippools',
|
|
2172
|
+
'globalnetworkpolicy', 'globalnetworkpolicies',
|
|
2173
|
+
'stagednetworkpolicy', 'stagednetworkpolicies',
|
|
2174
|
+
'stagedglobalnetworkpolicy', 'stagedglobalnetworkpolicies',
|
|
2175
|
+
'stagedkubernetesnetworkpolicy', 'stagedkubernetesnetworkpolicies',
|
|
2176
|
+
])
|
|
2177
|
+
const CALICO_ONLY_COLUMN_KEYS = new Set([
|
|
2178
|
+
'globalnetworkpolicy', 'globalnetworkpolicies',
|
|
2179
|
+
'stagednetworkpolicy', 'stagednetworkpolicies',
|
|
2180
|
+
'stagedglobalnetworkpolicy', 'stagedglobalnetworkpolicies',
|
|
2181
|
+
'stagedkubernetesnetworkpolicy', 'stagedkubernetesnetworkpolicies',
|
|
2182
|
+
])
|
|
2183
|
+
|
|
1887
2184
|
// Normalize a kind name to its plural API form used in KNOWN_COLUMNS keys.
|
|
1888
2185
|
// Handles CRD singular names from URLs: 'ScaledObject' → 'scaledobjects', 'NodePool' → 'nodepools'
|
|
1889
2186
|
// When group is provided, resolves collisions (e.g., 'services' + 'serving.knative.dev' → 'knativeservices')
|
|
@@ -1893,6 +2190,8 @@ function normalizeKindToPlural(kind: string, group?: string): string {
|
|
|
1893
2190
|
if (group && GROUP_QUALIFIED_COLUMN_KEYS[lower]?.[group]) {
|
|
1894
2191
|
return GROUP_QUALIFIED_COLUMN_KEYS[lower][group]
|
|
1895
2192
|
}
|
|
2193
|
+
if (group && GROUP_QUALIFIED_ONLY_COLUMN_KEYS.has(lower)) return `__generic_${lower}`
|
|
2194
|
+
if (CALICO_ONLY_COLUMN_KEYS.has(lower)) return `__generic_${lower}`
|
|
1896
2195
|
if (KNOWN_COLUMNS[lower]) return lower
|
|
1897
2196
|
// Try adding 's' but avoid double-s (e.g., "ingress" → "ingresss")
|
|
1898
2197
|
if (!lower.endsWith('s') && KNOWN_COLUMNS[lower + 's']) return lower + 's'
|
|
@@ -4928,6 +5227,11 @@ export function ResourcesView({
|
|
|
4928
5227
|
) : filteredResources.length === 0 ? (
|
|
4929
5228
|
<div className="absolute inset-0 flex flex-col items-center justify-center text-theme-text-tertiary">
|
|
4930
5229
|
<p>No {selectedKind.kind} found</p>
|
|
5230
|
+
{emptyKindNote(selectedKind.kind, selectedKind.group, resources?.length ?? 0) && (
|
|
5231
|
+
<p className="text-xs mt-2 max-w-md text-center text-theme-text-disabled">
|
|
5232
|
+
{emptyKindNote(selectedKind.kind, selectedKind.group, resources?.length ?? 0)}
|
|
5233
|
+
</p>
|
|
5234
|
+
)}
|
|
4931
5235
|
{searchTerm && (
|
|
4932
5236
|
<button
|
|
4933
5237
|
onClick={() => {
|
|
@@ -5692,6 +5996,8 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
|
|
|
5692
5996
|
return <PVCCell resource={resource} column={column} />
|
|
5693
5997
|
case 'rollouts':
|
|
5694
5998
|
return <RolloutCell resource={resource} column={column} />
|
|
5999
|
+
case 'analysisruns':
|
|
6000
|
+
return <AnalysisRunCell resource={resource} column={column} />
|
|
5695
6001
|
case 'workflows':
|
|
5696
6002
|
return <WorkflowCell resource={resource} column={column} />
|
|
5697
6003
|
case 'cronworkflows':
|
|
@@ -5731,7 +6037,21 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
|
|
|
5731
6037
|
case 'clusterworkflowtemplates':
|
|
5732
6038
|
return <WorkflowTemplateCell resource={resource} column={column} />
|
|
5733
6039
|
case 'networkpolicies':
|
|
6040
|
+
if (!isCoreNetworkPolicyKind(resource.kind ?? kind, resource.apiVersion, group)) {
|
|
6041
|
+
return <GenericCell resource={resource} column={column} />
|
|
6042
|
+
}
|
|
5734
6043
|
return <NetworkPolicyCell resource={resource} column={column} />
|
|
6044
|
+
case 'calicoippools':
|
|
6045
|
+
case 'calicohostendpoints':
|
|
6046
|
+
case 'calicotiers':
|
|
6047
|
+
return <CalicoInfraCell resource={resource} column={column} />
|
|
6048
|
+
case 'caliconetworkpolicies':
|
|
6049
|
+
case 'calicoglobalnetworkpolicies':
|
|
6050
|
+
case 'calicostagednetworkpolicies':
|
|
6051
|
+
case 'calicostagedglobalnetworkpolicies':
|
|
6052
|
+
case 'calicostagedkubernetesnetworkpolicies':
|
|
6053
|
+
if (!isCalicoPolicyResource(resource)) return <GenericCell resource={resource} column={column} />
|
|
6054
|
+
return <CalicoPolicyCell resource={resource} column={column} />
|
|
5735
6055
|
case 'poddisruptionbudgets':
|
|
5736
6056
|
return <PDBCell resource={resource} column={column} />
|
|
5737
6057
|
case 'serviceaccounts':
|
|
@@ -5848,6 +6168,42 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
|
|
|
5848
6168
|
case 'clustersecretstores':
|
|
5849
6169
|
return <ClusterSecretStoreCell resource={resource} column={column} />
|
|
5850
6170
|
// Velero
|
|
6171
|
+
// The cnpg* keys come from GROUP_QUALIFIED_COLUMN_KEYS; the unqualified
|
|
6172
|
+
// plurals below are reached when the caller had no group to resolve with,
|
|
6173
|
+
// and stay group-guarded so a foreign CRD is not read as CNPG.
|
|
6174
|
+
case 'updaterequests':
|
|
6175
|
+
return <KyvernoUpdateRequestCell resource={resource} column={column} />
|
|
6176
|
+
case 'ephemeralreports':
|
|
6177
|
+
case 'clusterephemeralreports':
|
|
6178
|
+
return <KyvernoEphemeralReportCell resource={resource} column={column} />
|
|
6179
|
+
case 'cnpgdatabases':
|
|
6180
|
+
case 'cnpgpublications':
|
|
6181
|
+
case 'cnpgsubscriptions':
|
|
6182
|
+
return <CNPGDeclarativeCell resource={resource} column={column} />
|
|
6183
|
+
case 'databases':
|
|
6184
|
+
case 'publications':
|
|
6185
|
+
if (isApiGroup(resource.apiVersion, CNPG_GROUP)) {
|
|
6186
|
+
return <CNPGDeclarativeCell resource={resource} column={column} />
|
|
6187
|
+
}
|
|
6188
|
+
return <GenericCell resource={resource} column={column} />
|
|
6189
|
+
case 'cnpgimagecatalogs':
|
|
6190
|
+
case 'cnpgclusterimagecatalogs':
|
|
6191
|
+
return <CNPGImageCatalogCell resource={resource} column={column} />
|
|
6192
|
+
case 'imagecatalogs':
|
|
6193
|
+
case 'clusterimagecatalogs':
|
|
6194
|
+
if (isApiGroup(resource.apiVersion, CNPG_GROUP)) {
|
|
6195
|
+
return <CNPGImageCatalogCell resource={resource} column={column} />
|
|
6196
|
+
}
|
|
6197
|
+
return <GenericCell resource={resource} column={column} />
|
|
6198
|
+
case 'barmanobjectstores':
|
|
6199
|
+
return <CNPGObjectStoreCell resource={resource} column={column} />
|
|
6200
|
+
case 'objectstores':
|
|
6201
|
+
// Group-guarded: another operator could serve an `objectstores` plural,
|
|
6202
|
+
// and it would have no recovery window to report.
|
|
6203
|
+
if (isApiGroup(resource.apiVersion, 'barmancloud.cnpg.io')) {
|
|
6204
|
+
return <CNPGObjectStoreCell resource={resource} column={column} />
|
|
6205
|
+
}
|
|
6206
|
+
return <GenericCell resource={resource} column={column} />
|
|
5851
6207
|
case 'cnpgbackups':
|
|
5852
6208
|
return <CNPGBackupCell resource={resource} column={column} />
|
|
5853
6209
|
case 'backups':
|
|
@@ -5936,8 +6292,19 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
|
|
|
5936
6292
|
return <ChannelCell resource={resource} column={column} />
|
|
5937
6293
|
case 'inmemorychannels':
|
|
5938
6294
|
return <InMemoryChannelCell resource={resource} column={column} />
|
|
5939
|
-
case '
|
|
6295
|
+
case 'knativesubscriptions':
|
|
5940
6296
|
return <SubscriptionCell resource={resource} column={column} />
|
|
6297
|
+
case 'subscriptions':
|
|
6298
|
+
// Shared plural, and not only by two: CNPG declares one, Knative declares
|
|
6299
|
+
// one, and a third operator's would be read as Knative's — Channel and
|
|
6300
|
+
// Subscriber blank on every row — unless both are matched positively.
|
|
6301
|
+
if (isApiGroup(resource.apiVersion, CNPG_GROUP)) {
|
|
6302
|
+
return <CNPGDeclarativeCell resource={resource} column={column} />
|
|
6303
|
+
}
|
|
6304
|
+
if (isApiGroup(resource.apiVersion, 'messaging.knative.dev')) {
|
|
6305
|
+
return <SubscriptionCell resource={resource} column={column} />
|
|
6306
|
+
}
|
|
6307
|
+
return <GenericCell resource={resource} column={column} />
|
|
5941
6308
|
// Knative Flows
|
|
5942
6309
|
case 'sequences':
|
|
5943
6310
|
return <SequenceCell resource={resource} column={column} />
|
|
@@ -7240,6 +7607,44 @@ function RolloutCell({ resource, column }: { resource: any; column: string }) {
|
|
|
7240
7607
|
}
|
|
7241
7608
|
}
|
|
7242
7609
|
|
|
7610
|
+
function AnalysisRunCell({ resource, column }: { resource: any; column: string }) {
|
|
7611
|
+
switch (column) {
|
|
7612
|
+
case 'status': {
|
|
7613
|
+
const status = getAnalysisRunStatus(resource)
|
|
7614
|
+
return <span className={clsx('badge', status.color)}>{status.text}</span>
|
|
7615
|
+
}
|
|
7616
|
+
case 'trigger': {
|
|
7617
|
+
const trigger = resource.metadata?.labels?.['rollout-type']
|
|
7618
|
+
const step = resource.metadata?.labels?.['step-index']
|
|
7619
|
+
if (!trigger) return <span className="text-sm text-theme-text-tertiary">-</span>
|
|
7620
|
+
return (
|
|
7621
|
+
<span className="text-sm text-theme-text-secondary">
|
|
7622
|
+
{trigger}
|
|
7623
|
+
{step ? ` (step ${step})` : ''}
|
|
7624
|
+
</span>
|
|
7625
|
+
)
|
|
7626
|
+
}
|
|
7627
|
+
case 'metrics': {
|
|
7628
|
+
const results: any[] = resource.status?.metricResults || []
|
|
7629
|
+
if (results.length === 0) return <span className="text-sm text-theme-text-tertiary">-</span>
|
|
7630
|
+
const { total, passing, notPassing } = summarizeAnalysisMetrics(resource)
|
|
7631
|
+
return (
|
|
7632
|
+
<Tooltip
|
|
7633
|
+
content={results
|
|
7634
|
+
.map((m) => `${m.name}: ${m.phase || 'Unknown'}${m.dryRun ? ' (dry-run)' : ''}`)
|
|
7635
|
+
.join('\n')}
|
|
7636
|
+
>
|
|
7637
|
+
<span className={clsx('text-sm', notPassing > 0 ? 'text-amber-400' : 'text-theme-text-secondary')}>
|
|
7638
|
+
{notPassing > 0 ? `${notPassing}/${total} not passing` : `${passing}/${total} passing`}
|
|
7639
|
+
</span>
|
|
7640
|
+
</Tooltip>
|
|
7641
|
+
)
|
|
7642
|
+
}
|
|
7643
|
+
default:
|
|
7644
|
+
return <span className="text-sm text-theme-text-tertiary">-</span>
|
|
7645
|
+
}
|
|
7646
|
+
}
|
|
7647
|
+
|
|
7243
7648
|
function WorkflowCell({ resource, column }: { resource: any; column: string }) {
|
|
7244
7649
|
switch (column) {
|
|
7245
7650
|
case 'status': {
|