@skyhook-io/k8s-ui 1.12.0 → 1.13.3

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 (117) hide show
  1. package/package.json +4 -4
  2. package/src/components/applications/ApplicationDetail.tsx +2 -2
  3. package/src/components/cluster-switcher/ClusterSwitcher.tsx +32 -7
  4. package/src/components/issues/severity.ts +1 -0
  5. package/src/components/resources/ResourcesView.empty-note.test.ts +41 -0
  6. package/src/components/resources/ResourcesView.tsx +419 -9
  7. package/src/components/resources/index.ts +27 -0
  8. package/src/components/resources/kyverno-legacy-action.test.ts +98 -0
  9. package/src/components/resources/renderers/AnalysisRunRenderer.test.tsx +241 -0
  10. package/src/components/resources/renderers/AnalysisRunRenderer.tsx +166 -0
  11. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +31 -2
  12. package/src/components/resources/renderers/CNPGClusterRenderer.test.tsx +33 -0
  13. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +47 -4
  14. package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +243 -0
  15. package/src/components/resources/renderers/CNPGObjectStoreRenderer.test.tsx +99 -0
  16. package/src/components/resources/renderers/CNPGObjectStoreRenderer.tsx +235 -0
  17. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +23 -1
  18. package/src/components/resources/renderers/CNPGShared.tsx +27 -0
  19. package/src/components/resources/renderers/CalicoHostEndpointRenderer.tsx +71 -0
  20. package/src/components/resources/renderers/CalicoIPPoolRenderer.tsx +32 -0
  21. package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.test.tsx +205 -0
  22. package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.tsx +537 -0
  23. package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.test.tsx +223 -0
  24. package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.tsx +670 -0
  25. package/src/components/resources/renderers/CalicoTierRenderer.tsx +19 -0
  26. package/src/components/resources/renderers/KyvernoCELPolicyRenderers.tsx +13 -6
  27. package/src/components/resources/renderers/KyvernoExceptionRenderers.tsx +3 -2
  28. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.test.ts +31 -0
  29. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +124 -0
  30. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +28 -2
  31. package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +303 -0
  32. package/src/components/resources/renderers/LookupFailureNote.test.tsx +60 -0
  33. package/src/components/resources/renderers/LookupFailureNote.tsx +42 -0
  34. package/src/components/resources/renderers/NamespaceRenderer.test.ts +30 -0
  35. package/src/components/resources/renderers/NamespaceRenderer.tsx +7 -3
  36. package/src/components/resources/renderers/NetworkPolicyDiagram.tsx +27 -26
  37. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -3
  38. package/src/components/resources/renderers/PolicyCoverageSection.test.tsx +348 -0
  39. package/src/components/resources/renderers/PolicyCoverageSection.tsx +939 -0
  40. package/src/components/resources/renderers/PolicySection.test.tsx +59 -2
  41. package/src/components/resources/renderers/PolicySection.tsx +32 -5
  42. package/src/components/resources/renderers/RolloutRenderer.test.tsx +491 -0
  43. package/src/components/resources/renderers/RolloutRenderer.tsx +391 -92
  44. package/src/components/resources/renderers/VeleroBSLRenderer.test.tsx +374 -0
  45. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +179 -3
  46. package/src/components/resources/renderers/VeleroBackupRenderer.test.tsx +221 -0
  47. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +119 -6
  48. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.test.tsx +69 -0
  49. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +96 -0
  50. package/src/components/resources/renderers/VeleroRestoreRenderer.test.tsx +55 -0
  51. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +22 -4
  52. package/src/components/resources/renderers/VeleroRunMessages.test.tsx +152 -0
  53. package/src/components/resources/renderers/VeleroRunMessages.tsx +139 -0
  54. package/src/components/resources/renderers/VeleroScheduleRenderer.test.tsx +88 -0
  55. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +16 -6
  56. package/src/components/resources/renderers/calico-cells.tsx +177 -0
  57. package/src/components/resources/renderers/cnpg-cells.tsx +100 -0
  58. package/src/components/resources/renderers/index.ts +16 -1
  59. package/src/components/resources/renderers/kyverno-cells.tsx +81 -0
  60. package/src/components/resources/renderers/network-policy-peer-styles.ts +25 -0
  61. package/src/components/resources/renderers/velero-cells.tsx +29 -7
  62. package/src/components/resources/renderers/velero-dead-ends.test.tsx +142 -0
  63. package/src/components/resources/resource-utils-calico.test.ts +215 -0
  64. package/src/components/resources/resource-utils-calico.ts +261 -0
  65. package/src/components/resources/resource-utils-cnpg-declarative.test.ts +104 -0
  66. package/src/components/resources/resource-utils-cnpg-objectstore.test.ts +122 -0
  67. package/src/components/resources/resource-utils-cnpg.test.ts +54 -0
  68. package/src/components/resources/resource-utils-cnpg.ts +264 -3
  69. package/src/components/resources/resource-utils-kyverno-modern.ts +24 -0
  70. package/src/components/resources/resource-utils-kyverno-queue.test.ts +164 -0
  71. package/src/components/resources/resource-utils-kyverno-queue.ts +198 -0
  72. package/src/components/resources/resource-utils-kyverno.ts +43 -7
  73. package/src/components/resources/resource-utils-velero.test.ts +26 -0
  74. package/src/components/resources/resource-utils-velero.ts +68 -0
  75. package/src/components/resources/resource-utils.ts +98 -0
  76. package/src/components/shared/ResourceActionsBar.tsx +101 -16
  77. package/src/components/shared/ResourceRendererDispatch.test.tsx +379 -3
  78. package/src/components/shared/ResourceRendererDispatch.tsx +199 -22
  79. package/src/components/shared/index.ts +1 -1
  80. package/src/components/shared/revision-role-badges.test.ts +120 -0
  81. package/src/components/topology/K8sResourceNode.test.ts +10 -0
  82. package/src/components/topology/K8sResourceNode.tsx +22 -0
  83. package/src/components/topology/TopologyFilterSidebar.test.tsx +31 -0
  84. package/src/components/topology/TopologyFilterSidebar.tsx +5 -0
  85. package/src/components/topology/TopologyGraph.tsx +6 -6
  86. package/src/components/topology/TopologySearch.tsx +7 -0
  87. package/src/components/topology/layout.ts +3 -0
  88. package/src/components/trace/ReachabilityView.tsx +1 -0
  89. package/src/components/trace/TracePanel.tsx +13 -2
  90. package/src/components/trace/reachFixtures.ts +26 -1
  91. package/src/components/trace/reachGraphModel.test.ts +20 -0
  92. package/src/components/trace/reachGraphModel.ts +8 -1
  93. package/src/components/trace/reachOrigins.test.ts +31 -0
  94. package/src/components/trace/reachOrigins.ts +11 -1
  95. package/src/components/ui/Badge.tsx +10 -0
  96. package/src/components/ui/InClusterConsentDialog.tsx +5 -4
  97. package/src/components/ui/ScheduleValue.test.tsx +80 -0
  98. package/src/components/ui/ScheduleValue.tsx +85 -0
  99. package/src/components/ui/SelectMenu.tsx +202 -28
  100. package/src/components/ui/drawer-components.test.tsx +17 -1
  101. package/src/components/ui/drawer-components.tsx +9 -2
  102. package/src/components/ui/index.ts +2 -0
  103. package/src/components/workload/WorkloadView.tsx +5 -2
  104. package/src/types/core.ts +36 -4
  105. package/src/types/policy.ts +153 -2
  106. package/src/utils/clipboard.test.ts +86 -0
  107. package/src/utils/clipboard.ts +50 -0
  108. package/src/utils/extended-resources.ts +4 -7
  109. package/src/utils/format.test.ts +83 -1
  110. package/src/utils/format.ts +80 -1
  111. package/src/utils/inClusterConsent.test.ts +9 -0
  112. package/src/utils/inClusterConsent.ts +9 -9
  113. package/src/utils/index.ts +1 -0
  114. package/src/utils/navigation.test.ts +5 -0
  115. package/src/utils/navigation.ts +5 -0
  116. package/src/utils/resource-icons.test.ts +49 -2
  117. package/src/utils/resource-icons.ts +29 -2
@@ -35,6 +35,7 @@ import type { SelectedResource, APIResource } from '../../types'
35
35
  import { isForbiddenError } from '../../types/fetch-error'
36
36
  import type { NavigateToResource } from '../../utils/navigation'
37
37
  import { categorizeResources, CORE_RESOURCES, findAPIResourceForRoute } from '../../utils/api-resources'
38
+ import { copyText } from '../../utils/clipboard'
38
39
  import {
39
40
  getPodStatus,
40
41
  getPodRestarts,
@@ -79,6 +80,8 @@ import {
79
80
  getPVCCapacity,
80
81
  getPVCAccessModes,
81
82
  getRolloutStatus,
83
+ getAnalysisRunStatus,
84
+ summarizeAnalysisMetrics,
82
85
  getRolloutStrategy,
83
86
  getRolloutReady,
84
87
  getRolloutStep,
@@ -137,6 +140,7 @@ import {
137
140
  import { SEVERITY_BADGE, EVENT_TYPE_COLORS } from '../../utils/badge-colors'
138
141
  import { pluralize } from '../../utils/pluralize'
139
142
  import { getPodGpuCount, getNodeGpuCount } from '../../utils/extended-resources'
143
+ import { parseQuantityToNumber } from '../../utils/format'
140
144
  import { type CustomColumnDef, type CustomColumnSource, customColumnKey, readCustomColumnValue, sanitizeCustomColumnDefs } from '../../utils/custom-columns'
141
145
  import { FreshnessControl, type FreshnessConnection } from '../ui/FreshnessControl'
142
146
  import { Tooltip } from '../ui/Tooltip'
@@ -152,14 +156,17 @@ import { ScaledObjectCell, ScaledJobCell, TriggerAuthenticationCell, ClusterTrig
152
156
  import { ResourceClaimCell, ResourceClaimTemplateCell, DeviceClassCell, ResourceSliceCell } from './renderers/dra-cells'
153
157
  import { NvidiaClusterPolicyCell, NvidiaDriverCell } from './renderers/nvidia-cells'
154
158
  import { ServiceMonitorCell, PrometheusRuleCell, PodMonitorCell } from './renderers/prometheus-cells'
155
- import { PolicyReportCell, ClusterPolicyReportCell, KyvernoPolicyCell, ClusterPolicyCell } from './renderers/kyverno-cells'
159
+ import { PolicyReportCell, ClusterPolicyReportCell, KyvernoPolicyCell, ClusterPolicyCell,
160
+ KyvernoUpdateRequestCell,
161
+ KyvernoEphemeralReportCell,
162
+ } from './renderers/kyverno-cells'
156
163
  import { KyvernoModernPolicyCell, KyvernoPolicyExceptionCell, KyvernoCleanupPolicyCell } from './renderers/kyverno-modern-cells'
157
164
  import { KYVERNO_MODERN_PLURALS, isModernKyvernoPolicy } from './resource-utils-kyverno-modern'
158
165
  import { isAnyKyvernoPolicyException } from './resource-utils-kyverno-exceptions'
159
166
  import { ExternalSecretCell, ClusterExternalSecretCell, SecretStoreCell, ClusterSecretStoreCell } from './renderers/eso-cells'
160
167
  import { BackupCell, RestoreCell, ScheduleCell, BackupStorageLocationCell, VolumeSnapshotLocationCell, BackupRepositoryCell } from './renderers/velero-cells'
161
168
  import { isVeleroResource } from './resource-utils-velero'
162
- import { CNPGClusterCell, CNPGBackupCell, CNPGScheduledBackupCell, CNPGPoolerCell } from './renderers/cnpg-cells'
169
+ import { CNPGClusterCell, CNPGBackupCell, CNPGScheduledBackupCell, CNPGPoolerCell, CNPGObjectStoreCell, CNPGDeclarativeCell, CNPGImageCatalogCell } from './renderers/cnpg-cells'
163
170
  import { isApiGroup, CNPG_GROUP } from './resource-utils-cnpg'
164
171
  import { ManagedResourceCell, CompositeResourceCell, CrossplaneProviderCell, CrossplaneProviderConfigCell, CompositionCell, XRDCell } from './renderers/crossplane-cells'
165
172
  import { isManagedResource, isComposite } from './resource-utils-crossplane'
@@ -171,6 +178,8 @@ import { CAPIClusterCell, CAPIMachineCell, CAPIMachineDeploymentCell, CAPIMachin
171
178
  import { AWSManagedControlPlaneCell, AWSManagedMachinePoolCell, AWSMachineCell, AWSMachineTemplateCell, AWSManagedClusterCell } from './renderers/aws-capi-cells'
172
179
  import { GCPManagedControlPlaneCell, GCPManagedMachinePoolCell, GCPMachineCell, GCPMachineTemplateCell, GCPManagedClusterCell } from './renderers/gcp-capi-cells'
173
180
  import { AzureManagedControlPlaneCell, AzureManagedMachinePoolCell, AzureMachineCell, AzureMachineTemplateCell, AzureManagedClusterCell } from './renderers/azure-capi-cells'
181
+ import { CalicoInfraCell, CalicoPolicyCell } from './renderers/calico-cells'
182
+ import { isCalicoPolicyResource, isCoreNetworkPolicyKind } from './resource-utils-calico'
174
183
  import { useRegisterShortcut, useRegisterShortcuts } from '../../hooks/useKeyboardShortcuts'
175
184
  import { ResourcesSidebar } from './ResourcesSidebar'
176
185
  import type { SelectedKindInfo } from './ResourcesSidebar'
@@ -274,10 +283,14 @@ function buildCustomColumn(d: CustomColumnDef): ExtraColumn {
274
283
  }
275
284
 
276
285
  // Tailwind width class → pixel minimum mapping for CSS Grid column sizing
286
+ // A width class missing from this map falls back to 80px, and nothing says so —
287
+ // the column just renders narrow enough to truncate its header. Keep the whole
288
+ // Tailwind step range present rather than only the sizes in use today.
277
289
  const TAILWIND_WIDTH_TO_PX: Record<string, number> = {
278
290
  'w-12': 48, 'w-14': 56, 'w-16': 64, 'w-20': 80, 'w-24': 96,
279
291
  '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,
292
+ 'w-48': 192, 'w-52': 208, 'w-56': 224, 'w-60': 240, 'w-64': 256,
293
+ 'w-72': 288, 'w-80': 320, 'w-96': 384,
281
294
  }
282
295
 
283
296
  const COMPARE_COLUMN_WIDTH = 36
@@ -296,6 +309,30 @@ function getColumnMinWidth(col: Column): number {
296
309
  }
297
310
 
298
311
  // Default columns for unknown resource types (CRDs)
312
+ /**
313
+ * Why a list of this kind being empty is not the answer it looks like.
314
+ *
315
+ * Kyverno's two working records are garbage-collected within seconds of
316
+ * finishing, so "none" is the resting state of a healthy cluster — and for
317
+ * UpdateRequest it is ALSO what you see when a generate rule never fired at
318
+ * all. Those are opposite conclusions and a bare "No X found" renders them
319
+ * identically.
320
+ */
321
+ export function emptyKindNote(kind: string, group?: string, unfilteredTotal = 0): string | undefined {
322
+ // A search or column filter emptying the table is the reader's own doing.
323
+ // Explaining that none is the resting state then answers a question they did
324
+ // not ask, while their own term is still in the box.
325
+ if (unfilteredTotal > 0) return undefined
326
+ const g = group ?? ''
327
+ if (kind.toLowerCase() === 'updaterequest' && g === 'kyverno.io') {
328
+ 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.'
329
+ }
330
+ if ((kind.toLowerCase() === 'ephemeralreport' || kind.toLowerCase() === 'clusterephemeralreport') && g === 'reports.kyverno.io') {
331
+ 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.'
332
+ }
333
+ return undefined
334
+ }
335
+
299
336
  const DEFAULT_COLUMNS: Column[] = [
300
337
  { key: 'name', label: 'Name' },
301
338
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
@@ -464,6 +501,14 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
464
501
  { key: 'images', label: 'Images', width: 'w-48', hideOnMobile: true },
465
502
  { key: 'age', label: 'Age', width: 'w-24' },
466
503
  ],
504
+ analysisruns: [
505
+ { key: 'name', label: 'Name' },
506
+ { key: 'namespace', label: 'Namespace', width: 'w-48' },
507
+ { key: 'status', label: 'Phase', width: 'w-28' },
508
+ { key: 'trigger', label: 'Trigger', width: 'w-32', tooltip: 'Which part of the Rollout started this run' },
509
+ { key: 'metrics', label: 'Metrics', width: 'w-40', hideOnMobile: true, tooltip: 'Per-metric verdicts' },
510
+ { key: 'age', label: 'Age', width: 'w-24' },
511
+ ],
467
512
  workflows: [
468
513
  { key: 'name', label: 'Name' },
469
514
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
@@ -913,6 +958,88 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
913
958
  { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
914
959
  { key: 'age', label: 'Age', width: 'w-24' },
915
960
  ],
961
+ caliconetworkpolicies: [
962
+ { key: 'name', label: 'Name' },
963
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
964
+ { key: 'selector', label: 'Selector', width: 'w-48' },
965
+ { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
966
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
967
+ { key: 'tier', label: 'Tier', width: 'w-28' },
968
+ { key: 'order', label: 'Order', width: 'w-24' },
969
+ { key: 'types', label: 'Types', width: 'w-28' },
970
+ { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
971
+ { key: 'age', label: 'Age', width: 'w-24' },
972
+ ],
973
+ calicoglobalnetworkpolicies: [
974
+ { key: 'name', label: 'Name' },
975
+ { key: 'selector', label: 'Selector', width: 'w-48' },
976
+ { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
977
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
978
+ { key: 'tier', label: 'Tier', width: 'w-28' },
979
+ { key: 'order', label: 'Order', width: 'w-24' },
980
+ { key: 'types', label: 'Types', width: 'w-28' },
981
+ { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
982
+ { key: 'age', label: 'Age', width: 'w-24' },
983
+ ],
984
+ calicostagednetworkpolicies: [
985
+ { key: 'name', label: 'Name' },
986
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
987
+ { key: 'selector', label: 'Selector', width: 'w-48' },
988
+ { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
989
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
990
+ { key: 'tier', label: 'Tier', width: 'w-28' },
991
+ { key: 'order', label: 'Order', width: 'w-24' },
992
+ { key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
993
+ { key: 'types', label: 'Types', width: 'w-28' },
994
+ { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
995
+ { key: 'age', label: 'Age', width: 'w-24' },
996
+ ],
997
+ calicostagedglobalnetworkpolicies: [
998
+ { key: 'name', label: 'Name' },
999
+ { key: 'selector', label: 'Selector', width: 'w-48' },
1000
+ { key: 'namespaceSelector', label: 'Namespace Selector', width: 'w-48', defaultVisible: false },
1001
+ { key: 'serviceAccountSelector', label: 'Service Account Selector', width: 'w-48', defaultVisible: false },
1002
+ { key: 'tier', label: 'Tier', width: 'w-28' },
1003
+ { key: 'order', label: 'Order', width: 'w-24' },
1004
+ { key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
1005
+ { key: 'types', label: 'Types', width: 'w-28' },
1006
+ { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1007
+ { key: 'age', label: 'Age', width: 'w-24' },
1008
+ ],
1009
+ calicoippools: [
1010
+ { key: 'name', label: 'Name' },
1011
+ { key: 'cidr', label: 'CIDR', width: 'w-44' },
1012
+ { key: 'blockSize', label: 'Block Size', width: 'w-28' },
1013
+ { key: 'encapsulation', label: 'Encapsulation', width: 'w-36' },
1014
+ { key: 'natOutgoing', label: 'NAT Outgoing', width: 'w-32' },
1015
+ { key: 'disabled', label: 'Disabled', width: 'w-28' },
1016
+ { key: 'allowedUses', label: 'Allowed Uses', width: 'w-40', defaultVisible: false },
1017
+ { key: 'nodeSelector', label: 'Node Selector', width: 'w-48', defaultVisible: false },
1018
+ { key: 'age', label: 'Age', width: 'w-24' },
1019
+ ],
1020
+ calicohostendpoints: [
1021
+ { key: 'name', label: 'Name' },
1022
+ { key: 'node', label: 'Node', width: 'w-48' },
1023
+ { key: 'interfaceName', label: 'Interface', width: 'w-32' },
1024
+ { key: 'expectedIPs', label: 'Expected IPs', width: 'w-48' },
1025
+ { key: 'profiles', label: 'Profiles', width: 'w-48', defaultVisible: false },
1026
+ { key: 'age', label: 'Age', width: 'w-24' },
1027
+ ],
1028
+ calicotiers: [
1029
+ { key: 'name', label: 'Name' },
1030
+ { key: 'order', label: 'Order', width: 'w-24' },
1031
+ { key: 'defaultAction', label: 'Default Action', width: 'w-36' },
1032
+ { key: 'age', label: 'Age', width: 'w-24' },
1033
+ ],
1034
+ calicostagedkubernetesnetworkpolicies: [
1035
+ { key: 'name', label: 'Name' },
1036
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
1037
+ { key: 'selector', label: 'Pod Selector', width: 'w-48' },
1038
+ { key: 'stagedAction', label: 'Staged Action', width: 'w-32' },
1039
+ { key: 'types', label: 'Types', width: 'w-28' },
1040
+ { key: 'rules', label: 'Rules', width: 'w-24', tooltip: 'Ingress / Egress rule count' },
1041
+ { key: 'age', label: 'Age', width: 'w-24' },
1042
+ ],
916
1043
  poddisruptionbudgets: [
917
1044
  { key: 'name', label: 'Name' },
918
1045
  { key: 'namespace', label: 'Namespace', width: 'w-48' },
@@ -1305,6 +1432,92 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1305
1432
  // than an absent one — the reader can't tell which meaning they're getting.
1306
1433
  { key: 'age', label: 'Age', width: 'w-24' },
1307
1434
  ],
1435
+ // `databases`, `publications` and `subscriptions` are crowded plurals — other
1436
+ // database operators ship all three, and Knative owns the unqualified
1437
+ // `subscriptions` set. Keyed under cnpg* and reached only through
1438
+ // GROUP_QUALIFIED_COLUMN_KEYS, so a foreign CRD gets the generic columns
1439
+ // instead of a Cluster column it can never fill.
1440
+ // Kyverno's working records. Both are keyed by things the reader has to see
1441
+ // to know whether anything is wrong: which policy queued the work and what it
1442
+ // was queued against, and which resource a scan is about.
1443
+ updaterequests: [
1444
+ { key: 'name', label: 'Name' },
1445
+ { key: 'namespace', label: 'Namespace', width: 'w-32' },
1446
+ { key: 'status', label: 'State', width: 'w-28' },
1447
+ { key: 'type', label: 'Type', width: 'w-24' },
1448
+ { key: 'policy', label: 'Policy', width: 'w-52' },
1449
+ { key: 'triggers', label: 'Triggered By', width: 'w-56' },
1450
+ { key: 'age', label: 'Age', width: 'w-24' },
1451
+ ],
1452
+ ephemeralreports: [
1453
+ { key: 'name', label: 'Name' },
1454
+ { key: 'namespace', label: 'Namespace', width: 'w-32' },
1455
+ { key: 'status', label: 'Outcome', width: 'w-28' },
1456
+ { key: 'subject', label: 'About', width: 'w-56' },
1457
+ { key: 'source', label: 'Produced By', width: 'w-36' },
1458
+ { key: 'results', label: 'Results', width: 'w-24' },
1459
+ { key: 'age', label: 'Age', width: 'w-24' },
1460
+ ],
1461
+ clusterephemeralreports: [
1462
+ { key: 'name', label: 'Name' },
1463
+ { key: 'status', label: 'Outcome', width: 'w-28' },
1464
+ { key: 'subject', label: 'About', width: 'w-56' },
1465
+ { key: 'source', label: 'Produced By', width: 'w-36' },
1466
+ { key: 'results', label: 'Results', width: 'w-24' },
1467
+ { key: 'age', label: 'Age', width: 'w-24' },
1468
+ ],
1469
+ cnpgdatabases: [
1470
+ { key: 'name', label: 'Name' },
1471
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
1472
+ { key: 'status', label: 'Applied', width: 'w-32' },
1473
+ { key: 'cluster', label: 'Cluster', width: 'w-36' },
1474
+ { key: 'target', label: 'Database', width: 'w-40' },
1475
+ { key: 'message', label: 'Message', width: 'w-64' },
1476
+ { key: 'age', label: 'Age', width: 'w-24' },
1477
+ ],
1478
+ cnpgpublications: [
1479
+ { key: 'name', label: 'Name' },
1480
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
1481
+ { key: 'status', label: 'Applied', width: 'w-32' },
1482
+ { key: 'cluster', label: 'Cluster', width: 'w-36' },
1483
+ { key: 'dbname', label: 'In Database', width: 'w-40' },
1484
+ { key: 'message', label: 'Message', width: 'w-64' },
1485
+ { key: 'age', label: 'Age', width: 'w-24' },
1486
+ ],
1487
+ cnpgsubscriptions: [
1488
+ { key: 'name', label: 'Name' },
1489
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
1490
+ { key: 'status', label: 'Applied', width: 'w-32' },
1491
+ { key: 'cluster', label: 'Cluster', width: 'w-36' },
1492
+ { key: 'dbname', label: 'In Database', width: 'w-40' },
1493
+ { key: 'message', label: 'Message', width: 'w-64' },
1494
+ { key: 'age', label: 'Age', width: 'w-24' },
1495
+ ],
1496
+ cnpgimagecatalogs: [
1497
+ { key: 'name', label: 'Name' },
1498
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
1499
+ { key: 'majors', label: 'Major Versions', width: 'w-40' },
1500
+ { key: 'images', label: 'Images', width: 'w-24' },
1501
+ { key: 'age', label: 'Age', width: 'w-24' },
1502
+ ],
1503
+ cnpgclusterimagecatalogs: [
1504
+ { key: 'name', label: 'Name' },
1505
+ { key: 'majors', label: 'Major Versions', width: 'w-40' },
1506
+ { key: 'images', label: 'Images', width: 'w-24' },
1507
+ { key: 'age', label: 'Age', width: 'w-24' },
1508
+ ],
1509
+ barmanobjectstores: [
1510
+ { key: 'name', label: 'Name' },
1511
+ { key: 'namespace', label: 'Namespace', width: 'w-36' },
1512
+ { key: 'status', label: 'Status', width: 'w-40' },
1513
+ // Sized rather than flexible: every sibling table gives its long value a
1514
+ // fixed width, and leaving two columns unsized let Name absorb the row at
1515
+ // 1920px — truncating the destination and pushing Retention off-screen.
1516
+ { key: 'destination', label: 'Destination', width: 'w-64' },
1517
+ { key: 'servers', label: 'Servers', width: 'w-32' },
1518
+ { key: 'retention', label: 'Retention', width: 'w-28' },
1519
+ { key: 'age', label: 'Age', width: 'w-24' },
1520
+ ],
1308
1521
  cnpgbackups: [
1309
1522
  { key: 'name', label: 'Name' },
1310
1523
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
@@ -1499,7 +1712,10 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1499
1712
  { key: 'address', label: 'Address', width: 'w-56' },
1500
1713
  { key: 'age', label: 'Age', width: 'w-24' },
1501
1714
  ],
1502
- subscriptions: [
1715
+ // Keyed under knative*, reached only through GROUP_QUALIFIED_COLUMN_KEYS, so a
1716
+ // third operator's `subscriptions` gets the generic columns instead of Channel
1717
+ // and Subscriber it can never fill.
1718
+ knativesubscriptions: [
1503
1719
  { key: 'name', label: 'Name' },
1504
1720
  { key: 'namespace', label: 'Namespace', width: 'w-36' },
1505
1721
  { key: 'status', label: 'Status', width: 'w-28' },
@@ -1869,6 +2085,16 @@ const KNOWN_COLUMNS: Record<string, Column[]> = {
1869
2085
 
1870
2086
  // Map (plural, group) → KNOWN_COLUMNS key for kinds that collide with core K8s
1871
2087
  const GROUP_QUALIFIED_COLUMN_KEYS: Record<string, Record<string, string>> = {
2088
+ networkpolicy: {
2089
+ 'networking.k8s.io': 'networkpolicies',
2090
+ 'projectcalico.org': 'caliconetworkpolicies',
2091
+ 'crd.projectcalico.org': 'caliconetworkpolicies',
2092
+ },
2093
+ networkpolicies: {
2094
+ 'networking.k8s.io': 'networkpolicies',
2095
+ 'projectcalico.org': 'caliconetworkpolicies',
2096
+ 'crd.projectcalico.org': 'caliconetworkpolicies',
2097
+ },
1872
2098
  clusters: { 'postgresql.cnpg.io': 'cnpgclusters', 'cluster.x-k8s.io': 'capiclusters' },
1873
2099
  // Velero owns the unqualified `backups` column set; CNPG Backups carry a
1874
2100
  // completely different shape (cluster + method, no storage location/expiry).
@@ -1882,8 +2108,81 @@ const GROUP_QUALIFIED_COLUMN_KEYS: Record<string, Record<string, string>> = {
1882
2108
  certificates: { 'networking.internal.knative.dev': 'knativecertificates' },
1883
2109
  restores: { 'velero.io': 'velerorestores' },
1884
2110
  schedules: { 'velero.io': 'veleroschedules' },
2111
+ // CNPG's declarative objects. Knative owns the unqualified `subscriptions`
2112
+ // set, and `databases` / `publications` are shipped by several database
2113
+ // operators — none of which have a CNPG Cluster to point at.
2114
+ databases: { 'postgresql.cnpg.io': 'cnpgdatabases' },
2115
+ publications: { 'postgresql.cnpg.io': 'cnpgpublications' },
2116
+ subscriptions: {
2117
+ 'postgresql.cnpg.io': 'cnpgsubscriptions',
2118
+ 'messaging.knative.dev': 'knativesubscriptions',
2119
+ },
2120
+ imagecatalogs: { 'postgresql.cnpg.io': 'cnpgimagecatalogs' },
2121
+ clusterimagecatalogs: { 'postgresql.cnpg.io': 'cnpgclusterimagecatalogs' },
2122
+ objectstores: { 'barmancloud.cnpg.io': 'barmanobjectstores' },
2123
+ ippools: {
2124
+ 'projectcalico.org': 'calicoippools',
2125
+ 'crd.projectcalico.org': 'calicoippools',
2126
+ },
2127
+ hostendpoints: {
2128
+ 'projectcalico.org': 'calicohostendpoints',
2129
+ 'crd.projectcalico.org': 'calicohostendpoints',
2130
+ },
2131
+ tiers: {
2132
+ 'projectcalico.org': 'calicotiers',
2133
+ 'crd.projectcalico.org': 'calicotiers',
2134
+ },
2135
+ globalnetworkpolicies: {
2136
+ 'projectcalico.org': 'calicoglobalnetworkpolicies',
2137
+ 'crd.projectcalico.org': 'calicoglobalnetworkpolicies',
2138
+ },
2139
+ globalnetworkpolicy: {
2140
+ 'projectcalico.org': 'calicoglobalnetworkpolicies',
2141
+ 'crd.projectcalico.org': 'calicoglobalnetworkpolicies',
2142
+ },
2143
+ stagednetworkpolicies: {
2144
+ 'projectcalico.org': 'calicostagednetworkpolicies',
2145
+ 'crd.projectcalico.org': 'calicostagednetworkpolicies',
2146
+ },
2147
+ stagednetworkpolicy: {
2148
+ 'projectcalico.org': 'calicostagednetworkpolicies',
2149
+ 'crd.projectcalico.org': 'calicostagednetworkpolicies',
2150
+ },
2151
+ stagedglobalnetworkpolicies: {
2152
+ 'projectcalico.org': 'calicostagedglobalnetworkpolicies',
2153
+ 'crd.projectcalico.org': 'calicostagedglobalnetworkpolicies',
2154
+ },
2155
+ stagedglobalnetworkpolicy: {
2156
+ 'projectcalico.org': 'calicostagedglobalnetworkpolicies',
2157
+ 'crd.projectcalico.org': 'calicostagedglobalnetworkpolicies',
2158
+ },
2159
+ stagedkubernetesnetworkpolicies: {
2160
+ 'projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
2161
+ 'crd.projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
2162
+ },
2163
+ stagedkubernetesnetworkpolicy: {
2164
+ 'projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
2165
+ 'crd.projectcalico.org': 'calicostagedkubernetesnetworkpolicies',
2166
+ },
1885
2167
  }
1886
2168
 
2169
+ const GROUP_QUALIFIED_ONLY_COLUMN_KEYS = new Set([
2170
+ 'networkpolicy', 'networkpolicies',
2171
+ // Other projects serve their own IPPool CRD, so only a Calico group earns the
2172
+ // Calico columns.
2173
+ 'ippool', 'ippools',
2174
+ 'globalnetworkpolicy', 'globalnetworkpolicies',
2175
+ 'stagednetworkpolicy', 'stagednetworkpolicies',
2176
+ 'stagedglobalnetworkpolicy', 'stagedglobalnetworkpolicies',
2177
+ 'stagedkubernetesnetworkpolicy', 'stagedkubernetesnetworkpolicies',
2178
+ ])
2179
+ const CALICO_ONLY_COLUMN_KEYS = new Set([
2180
+ 'globalnetworkpolicy', 'globalnetworkpolicies',
2181
+ 'stagednetworkpolicy', 'stagednetworkpolicies',
2182
+ 'stagedglobalnetworkpolicy', 'stagedglobalnetworkpolicies',
2183
+ 'stagedkubernetesnetworkpolicy', 'stagedkubernetesnetworkpolicies',
2184
+ ])
2185
+
1887
2186
  // Normalize a kind name to its plural API form used in KNOWN_COLUMNS keys.
1888
2187
  // Handles CRD singular names from URLs: 'ScaledObject' → 'scaledobjects', 'NodePool' → 'nodepools'
1889
2188
  // When group is provided, resolves collisions (e.g., 'services' + 'serving.knative.dev' → 'knativeservices')
@@ -1893,6 +2192,8 @@ function normalizeKindToPlural(kind: string, group?: string): string {
1893
2192
  if (group && GROUP_QUALIFIED_COLUMN_KEYS[lower]?.[group]) {
1894
2193
  return GROUP_QUALIFIED_COLUMN_KEYS[lower][group]
1895
2194
  }
2195
+ if (group && GROUP_QUALIFIED_ONLY_COLUMN_KEYS.has(lower)) return `__generic_${lower}`
2196
+ if (CALICO_ONLY_COLUMN_KEYS.has(lower)) return `__generic_${lower}`
1896
2197
  if (KNOWN_COLUMNS[lower]) return lower
1897
2198
  // Try adding 's' but avoid double-s (e.g., "ingress" → "ingresss")
1898
2199
  if (!lower.endsWith('s') && KNOWN_COLUMNS[lower + 's']) return lower + 's'
@@ -4928,6 +5229,11 @@ export function ResourcesView({
4928
5229
  ) : filteredResources.length === 0 ? (
4929
5230
  <div className="absolute inset-0 flex flex-col items-center justify-center text-theme-text-tertiary">
4930
5231
  <p>No {selectedKind.kind} found</p>
5232
+ {emptyKindNote(selectedKind.kind, selectedKind.group, resources?.length ?? 0) && (
5233
+ <p className="text-xs mt-2 max-w-md text-center text-theme-text-disabled">
5234
+ {emptyKindNote(selectedKind.kind, selectedKind.group, resources?.length ?? 0)}
5235
+ </p>
5236
+ )}
4931
5237
  {searchTerm && (
4932
5238
  <button
4933
5239
  onClick={() => {
@@ -5534,10 +5840,11 @@ function CopyNameButton({ name }: { name: string }) {
5534
5840
  <button
5535
5841
  onClick={(e) => {
5536
5842
  e.stopPropagation()
5537
- navigator.clipboard.writeText(name).then(() => {
5843
+ void copyText(name).then((didCopy) => {
5844
+ if (!didCopy) return
5538
5845
  setCopied(true)
5539
5846
  setTimeout(() => setCopied(false), 1500)
5540
- }).catch(() => {})
5847
+ })
5541
5848
  }}
5542
5849
  className="shrink-0 p-0.5 text-theme-text-tertiary hover:text-theme-text-primary opacity-0 group-hover/row:opacity-100 transition-opacity"
5543
5850
  title="Copy name"
@@ -5692,6 +5999,8 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
5692
5999
  return <PVCCell resource={resource} column={column} />
5693
6000
  case 'rollouts':
5694
6001
  return <RolloutCell resource={resource} column={column} />
6002
+ case 'analysisruns':
6003
+ return <AnalysisRunCell resource={resource} column={column} />
5695
6004
  case 'workflows':
5696
6005
  return <WorkflowCell resource={resource} column={column} />
5697
6006
  case 'cronworkflows':
@@ -5731,7 +6040,21 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
5731
6040
  case 'clusterworkflowtemplates':
5732
6041
  return <WorkflowTemplateCell resource={resource} column={column} />
5733
6042
  case 'networkpolicies':
6043
+ if (!isCoreNetworkPolicyKind(resource.kind ?? kind, resource.apiVersion, group)) {
6044
+ return <GenericCell resource={resource} column={column} />
6045
+ }
5734
6046
  return <NetworkPolicyCell resource={resource} column={column} />
6047
+ case 'calicoippools':
6048
+ case 'calicohostendpoints':
6049
+ case 'calicotiers':
6050
+ return <CalicoInfraCell resource={resource} column={column} />
6051
+ case 'caliconetworkpolicies':
6052
+ case 'calicoglobalnetworkpolicies':
6053
+ case 'calicostagednetworkpolicies':
6054
+ case 'calicostagedglobalnetworkpolicies':
6055
+ case 'calicostagedkubernetesnetworkpolicies':
6056
+ if (!isCalicoPolicyResource(resource)) return <GenericCell resource={resource} column={column} />
6057
+ return <CalicoPolicyCell resource={resource} column={column} />
5735
6058
  case 'poddisruptionbudgets':
5736
6059
  return <PDBCell resource={resource} column={column} />
5737
6060
  case 'serviceaccounts':
@@ -5848,6 +6171,42 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
5848
6171
  case 'clustersecretstores':
5849
6172
  return <ClusterSecretStoreCell resource={resource} column={column} />
5850
6173
  // Velero
6174
+ // The cnpg* keys come from GROUP_QUALIFIED_COLUMN_KEYS; the unqualified
6175
+ // plurals below are reached when the caller had no group to resolve with,
6176
+ // and stay group-guarded so a foreign CRD is not read as CNPG.
6177
+ case 'updaterequests':
6178
+ return <KyvernoUpdateRequestCell resource={resource} column={column} />
6179
+ case 'ephemeralreports':
6180
+ case 'clusterephemeralreports':
6181
+ return <KyvernoEphemeralReportCell resource={resource} column={column} />
6182
+ case 'cnpgdatabases':
6183
+ case 'cnpgpublications':
6184
+ case 'cnpgsubscriptions':
6185
+ return <CNPGDeclarativeCell resource={resource} column={column} />
6186
+ case 'databases':
6187
+ case 'publications':
6188
+ if (isApiGroup(resource.apiVersion, CNPG_GROUP)) {
6189
+ return <CNPGDeclarativeCell resource={resource} column={column} />
6190
+ }
6191
+ return <GenericCell resource={resource} column={column} />
6192
+ case 'cnpgimagecatalogs':
6193
+ case 'cnpgclusterimagecatalogs':
6194
+ return <CNPGImageCatalogCell resource={resource} column={column} />
6195
+ case 'imagecatalogs':
6196
+ case 'clusterimagecatalogs':
6197
+ if (isApiGroup(resource.apiVersion, CNPG_GROUP)) {
6198
+ return <CNPGImageCatalogCell resource={resource} column={column} />
6199
+ }
6200
+ return <GenericCell resource={resource} column={column} />
6201
+ case 'barmanobjectstores':
6202
+ return <CNPGObjectStoreCell resource={resource} column={column} />
6203
+ case 'objectstores':
6204
+ // Group-guarded: another operator could serve an `objectstores` plural,
6205
+ // and it would have no recovery window to report.
6206
+ if (isApiGroup(resource.apiVersion, 'barmancloud.cnpg.io')) {
6207
+ return <CNPGObjectStoreCell resource={resource} column={column} />
6208
+ }
6209
+ return <GenericCell resource={resource} column={column} />
5851
6210
  case 'cnpgbackups':
5852
6211
  return <CNPGBackupCell resource={resource} column={column} />
5853
6212
  case 'backups':
@@ -5936,8 +6295,19 @@ function CellContent({ resource, kind, column, group, majorityNodeMinorVersion,
5936
6295
  return <ChannelCell resource={resource} column={column} />
5937
6296
  case 'inmemorychannels':
5938
6297
  return <InMemoryChannelCell resource={resource} column={column} />
5939
- case 'subscriptions':
6298
+ case 'knativesubscriptions':
5940
6299
  return <SubscriptionCell resource={resource} column={column} />
6300
+ case 'subscriptions':
6301
+ // Shared plural, and not only by two: CNPG declares one, Knative declares
6302
+ // one, and a third operator's would be read as Knative's — Channel and
6303
+ // Subscriber blank on every row — unless both are matched positively.
6304
+ if (isApiGroup(resource.apiVersion, CNPG_GROUP)) {
6305
+ return <CNPGDeclarativeCell resource={resource} column={column} />
6306
+ }
6307
+ if (isApiGroup(resource.apiVersion, 'messaging.knative.dev')) {
6308
+ return <SubscriptionCell resource={resource} column={column} />
6309
+ }
6310
+ return <GenericCell resource={resource} column={column} />
5941
6311
  // Knative Flows
5942
6312
  case 'sequences':
5943
6313
  return <SequenceCell resource={resource} column={column} />
@@ -7151,8 +7521,10 @@ function NodeCell({ resource, column, majorityNodeMinorVersion }: { resource: an
7151
7521
  const allocatable = resource.status?.allocatable?.pods
7152
7522
  const podCount = m?.podCount ?? 0
7153
7523
  if (!allocatable) return <span className="text-sm text-theme-text-tertiary font-mono">{podCount || '-'}</span>
7154
- const max = parseInt(allocatable, 10)
7155
- if (isNaN(max) || max <= 0) return <span className="text-sm text-theme-text-tertiary font-mono">{podCount || '-'}</span>
7524
+ // Allocatable pods is a Quantity, so a 1000-pod node reports "1k" —
7525
+ // a raw-integer read would see 1 and peg the bar at 21600%.
7526
+ const max = parseQuantityToNumber(allocatable)
7527
+ if (!Number.isFinite(max) || max <= 0) return <span className="text-sm text-theme-text-tertiary font-mono">{podCount || '-'}</span>
7156
7528
  const pct = (podCount / max) * 100
7157
7529
  return <ResourceBar used={String(podCount)} total={String(max)} percent={pct} colorScheme="count" />
7158
7530
  }
@@ -7240,6 +7612,44 @@ function RolloutCell({ resource, column }: { resource: any; column: string }) {
7240
7612
  }
7241
7613
  }
7242
7614
 
7615
+ function AnalysisRunCell({ resource, column }: { resource: any; column: string }) {
7616
+ switch (column) {
7617
+ case 'status': {
7618
+ const status = getAnalysisRunStatus(resource)
7619
+ return <span className={clsx('badge', status.color)}>{status.text}</span>
7620
+ }
7621
+ case 'trigger': {
7622
+ const trigger = resource.metadata?.labels?.['rollout-type']
7623
+ const step = resource.metadata?.labels?.['step-index']
7624
+ if (!trigger) return <span className="text-sm text-theme-text-tertiary">-</span>
7625
+ return (
7626
+ <span className="text-sm text-theme-text-secondary">
7627
+ {trigger}
7628
+ {step ? ` (step ${step})` : ''}
7629
+ </span>
7630
+ )
7631
+ }
7632
+ case 'metrics': {
7633
+ const results: any[] = resource.status?.metricResults || []
7634
+ if (results.length === 0) return <span className="text-sm text-theme-text-tertiary">-</span>
7635
+ const { total, passing, notPassing } = summarizeAnalysisMetrics(resource)
7636
+ return (
7637
+ <Tooltip
7638
+ content={results
7639
+ .map((m) => `${m.name}: ${m.phase || 'Unknown'}${m.dryRun ? ' (dry-run)' : ''}`)
7640
+ .join('\n')}
7641
+ >
7642
+ <span className={clsx('text-sm', notPassing > 0 ? 'text-amber-400' : 'text-theme-text-secondary')}>
7643
+ {notPassing > 0 ? `${notPassing}/${total} not passing` : `${passing}/${total} passing`}
7644
+ </span>
7645
+ </Tooltip>
7646
+ )
7647
+ }
7648
+ default:
7649
+ return <span className="text-sm text-theme-text-tertiary">-</span>
7650
+ }
7651
+ }
7652
+
7243
7653
  function WorkflowCell({ resource, column }: { resource: any; column: string }) {
7244
7654
  switch (column) {
7245
7655
  case 'status': {
@@ -3,6 +3,33 @@ export * from './resource-utils-hpa'
3
3
  export * from './resource-utils-argo'
4
4
  export * from './resource-utils-certmanager'
5
5
  export * from './resource-utils-cnpg'
6
+ export {
7
+ CALICO_GROUPS,
8
+ type CalicoApiGroup,
9
+ isCalicoApiGroup,
10
+ isCalicoApiVersion,
11
+ isCalicoPolicyKind,
12
+ isCalicoNetworkPolicyKind,
13
+ isCalicoStagedKubernetesNetworkPolicyKind,
14
+ formatKubernetesLabelSelector,
15
+ isCalicoPolicyResource,
16
+ isCoreNetworkPolicyApiVersion,
17
+ isCoreNetworkPolicyResource,
18
+ isCoreNetworkPolicyKind,
19
+ getCalicoPolicyKindLabel,
20
+ getCalicoPolicyTypes,
21
+ getCalicoPolicyRuleCount,
22
+ getCalicoPolicySelector,
23
+ getCalicoIPPoolAllowedUses,
24
+ getCalicoIPPoolBlockSize,
25
+ getCalicoIPPoolEncapsulation,
26
+ getCalicoStagedAction,
27
+ isCalicoStagedDeletion,
28
+ isCalicoStagedIgnored,
29
+ CALICO_SELECTOR_NOT_APPLICABLE,
30
+ getCalicoPolicyNamespaceSelector,
31
+ getCalicoPolicyServiceAccountSelector,
32
+ } from './resource-utils-calico'
6
33
  export * from './resource-utils-crossplane'
7
34
  export * from './resource-utils-eso'
8
35
  export * from './resource-utils-flux'