@skyhook-io/k8s-ui 1.16.3 → 1.17.1

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 (309) hide show
  1. package/README.md +41 -0
  2. package/package.json +3 -3
  3. package/src/components/applications/ApplicationDetail.test.tsx +15 -0
  4. package/src/components/applications/ApplicationDetail.tsx +17 -15
  5. package/src/components/charts/AreaChart.interaction.test.tsx +118 -0
  6. package/src/components/charts/AreaChart.test.tsx +25 -0
  7. package/src/components/charts/AreaChart.tsx +141 -12
  8. package/src/components/charts/index.ts +1 -0
  9. package/src/components/checks/ChecksView.tsx +18 -1
  10. package/src/components/checks/types.ts +2 -13
  11. package/src/components/cnpg/CNPGBackupSummary.tsx +57 -9
  12. package/src/components/cnpg/CNPGClusterHASection.test.tsx +90 -0
  13. package/src/components/cnpg/CNPGClusterHASection.tsx +331 -0
  14. package/src/components/cnpg/CNPGClusterSummary.test.tsx +275 -0
  15. package/src/components/cnpg/CNPGClusterSummary.tsx +310 -96
  16. package/src/components/cnpg/CNPGConnectSection.test.tsx +64 -0
  17. package/src/components/cnpg/CNPGConnectSection.tsx +178 -0
  18. package/src/components/cnpg/CNPGDeclarativeSummary.tsx +157 -26
  19. package/src/components/cnpg/CNPGImageCatalogSummary.tsx +7 -4
  20. package/src/components/cnpg/CNPGLogicalPath.test.tsx +22 -0
  21. package/src/components/cnpg/CNPGLogicalPath.tsx +150 -0
  22. package/src/components/cnpg/CNPGObjectStoreSummary.tsx +9 -6
  23. package/src/components/cnpg/CNPGObjectSummary.test.tsx +193 -2
  24. package/src/components/cnpg/CNPGPoolerScheduling.test.tsx +36 -0
  25. package/src/components/cnpg/CNPGPoolerSummary.tsx +241 -14
  26. package/src/components/cnpg/CNPGSharedSummary.tsx +15 -8
  27. package/src/components/cnpg/CNPGWALArchivingFact.test.tsx +39 -0
  28. package/src/components/cnpg/backupRuns.test.ts +17 -0
  29. package/src/components/cnpg/backupRuns.ts +19 -0
  30. package/src/components/cnpg/connect.test.ts +124 -0
  31. package/src/components/cnpg/connect.ts +155 -0
  32. package/src/components/cnpg/databaseRole.test.ts +38 -0
  33. package/src/components/cnpg/databaseRole.ts +80 -0
  34. package/src/components/cnpg/ha.test.ts +446 -0
  35. package/src/components/cnpg/ha.ts +621 -0
  36. package/src/components/cnpg/index.ts +18 -0
  37. package/src/components/cnpg/logicalReplication.test.ts +121 -0
  38. package/src/components/cnpg/logicalReplication.ts +252 -0
  39. package/src/components/cnpg/pooler.test.ts +84 -0
  40. package/src/components/cnpg/pooler.ts +225 -0
  41. package/src/components/cnpg/primitives.tsx +21 -117
  42. package/src/components/cnpg/problems.ts +12 -0
  43. package/src/components/cnpg/relations.test.ts +63 -13
  44. package/src/components/cnpg/relations.ts +61 -52
  45. package/src/components/cnpg/schedule.ts +35 -0
  46. package/src/components/cnpg/workspace-disk.test.ts +97 -0
  47. package/src/components/cnpg/workspace-fleet-metrics.test.ts +250 -0
  48. package/src/components/cnpg/workspace-problems.test.ts +175 -0
  49. package/src/components/cnpg/workspace.test.ts +380 -9
  50. package/src/components/cnpg/workspace.ts +1137 -136
  51. package/src/components/dock/DockContext.test.tsx +32 -0
  52. package/src/components/dock/DockContext.tsx +18 -4
  53. package/src/components/dock/LocalTerminalTab.test.tsx +40 -0
  54. package/src/components/dock/LocalTerminalTab.tsx +4 -3
  55. package/src/components/dock/TerminalTab.tsx +20 -8
  56. package/src/components/facts/certainty.tsx +36 -0
  57. package/src/components/facts/facts.test.tsx +24 -0
  58. package/src/components/facts/facts.tsx +62 -0
  59. package/src/components/facts/index.ts +6 -0
  60. package/src/components/facts/managed-by.test.tsx +17 -0
  61. package/src/components/facts/managed-by.tsx +36 -0
  62. package/src/components/facts/public-exports.test.tsx +35 -0
  63. package/src/components/gitops/GitOpsGraphFilterRail.tsx +6 -2
  64. package/src/components/gitops/GitOpsHealthSourceNotice.test.tsx +38 -0
  65. package/src/components/gitops/GitOpsHealthSourceNotice.tsx +13 -3
  66. package/src/components/gitops/GitOpsTableView.tsx +9 -24
  67. package/src/components/gitops/detail-helpers.ts +1 -1
  68. package/src/components/gitops/insights/ArgoResourceDiff.test.tsx +115 -0
  69. package/src/components/gitops/insights/ArgoResourceDiff.tsx +22 -5
  70. package/src/components/gitops/insights/DriftPanel.tsx +62 -0
  71. package/src/components/gitops/insights/GitOpsInsightViews.tsx +29 -83
  72. package/src/components/gitops/tree/GitOpsTreeGraph.test.ts +26 -1
  73. package/src/components/gitops/tree/GitOpsTreeGraph.tsx +40 -60
  74. package/src/components/gitops/tree/layout.test.ts +111 -0
  75. package/src/components/gitops/tree/layout.ts +128 -0
  76. package/src/components/gitops/tree/tree-helpers.test.ts +17 -0
  77. package/src/components/gitops/tree/tree-helpers.ts +7 -1
  78. package/src/components/issues/ConditionalCleanup.test.tsx +82 -0
  79. package/src/components/issues/IssuesView.tsx +40 -5
  80. package/src/components/issues/ResourceIssuesSection.test.tsx +28 -0
  81. package/src/components/issues/ResourceIssuesSection.tsx +3 -0
  82. package/src/components/issues/index.ts +3 -1
  83. package/src/components/issues/issue-timing.ts +14 -3
  84. package/src/components/issues/issues.test.ts +50 -2
  85. package/src/components/issues/severity.ts +21 -0
  86. package/src/components/issues/types.ts +23 -0
  87. package/src/components/logs/LogCore.theme.test.tsx +81 -3
  88. package/src/components/logs/LogCore.tsx +25 -17
  89. package/src/components/logs/LogToolbarSelects.tsx +3 -1
  90. package/src/components/logs/LogsViewer.test.tsx +291 -0
  91. package/src/components/logs/LogsViewer.tsx +72 -11
  92. package/src/components/logs/StructuredLogLine.test.tsx +76 -0
  93. package/src/components/logs/StructuredLogLine.tsx +22 -30
  94. package/src/components/logs/WorkloadLogsViewer.notice.test.tsx +59 -0
  95. package/src/components/logs/WorkloadLogsViewer.test.tsx +65 -0
  96. package/src/components/logs/WorkloadLogsViewer.tsx +44 -10
  97. package/src/components/logs/useLogBuffer.test.ts +8 -0
  98. package/src/components/logs/useLogStream.ts +28 -1
  99. package/src/components/problems/index.ts +1 -0
  100. package/src/components/problems/problems.test.tsx +63 -0
  101. package/src/components/problems/problems.tsx +165 -0
  102. package/src/components/resources/ActiveFilterBar.test.tsx +78 -0
  103. package/src/components/resources/ActiveFilterBar.tsx +86 -0
  104. package/src/components/resources/ResourcesSidebar.keyboard.test.tsx +42 -0
  105. package/src/components/resources/ResourcesSidebar.test.tsx +36 -2
  106. package/src/components/resources/ResourcesSidebar.tsx +92 -41
  107. package/src/components/resources/ResourcesView.kind-filter-memory.test.ts +52 -0
  108. package/src/components/resources/ResourcesView.kind-view.test.tsx +149 -0
  109. package/src/components/resources/ResourcesView.per-container.test.ts +56 -1
  110. package/src/components/resources/ResourcesView.tsx +309 -117
  111. package/src/components/resources/curated-column-ownership.test.ts +3 -3
  112. package/src/components/resources/renderers/AnalysisRunRenderer.tsx +3 -3
  113. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +6 -5
  114. package/src/components/resources/renderers/AutoscalingReferences.test.tsx +286 -0
  115. package/src/components/resources/renderers/CAPIClusterRenderer.tsx +5 -4
  116. package/src/components/resources/renderers/CAPIExactReferenceNavigation.test.tsx +27 -0
  117. package/src/components/resources/renderers/CAPIKubeadmControlPlaneRenderer.tsx +2 -2
  118. package/src/components/resources/renderers/CAPIMachineDeploymentRenderer.tsx +2 -2
  119. package/src/components/resources/renderers/CAPIMachinePoolRenderer.tsx +2 -2
  120. package/src/components/resources/renderers/CAPIMachineRenderer.tsx +2 -2
  121. package/src/components/resources/renderers/CAPIMachineSetRenderer.tsx +2 -2
  122. package/src/components/resources/renderers/CNPGBackupRenderer.test.tsx +23 -0
  123. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +8 -19
  124. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +7 -8
  125. package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +25 -0
  126. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.test.tsx +32 -0
  127. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +9 -17
  128. package/src/components/resources/renderers/CNPGShared.tsx +2 -2
  129. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -2
  130. package/src/components/resources/renderers/CompositeRenderer.navigation.test.tsx +35 -0
  131. package/src/components/resources/renderers/CompositeRenderer.tsx +3 -3
  132. package/src/components/resources/renderers/CompositionRenderer.tsx +3 -3
  133. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -2
  134. package/src/components/resources/renderers/ContourExactReferenceNavigation.test.tsx +34 -0
  135. package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +11 -10
  136. package/src/components/resources/renderers/CronJobRenderer.tsx +6 -4
  137. package/src/components/resources/renderers/CronWorkflowRenderer.tsx +3 -2
  138. package/src/components/resources/renderers/EndpointSliceRenderer.test.tsx +62 -0
  139. package/src/components/resources/renderers/EndpointSliceRenderer.tsx +15 -14
  140. package/src/components/resources/renderers/EventRenderer.tsx +3 -3
  141. package/src/components/resources/renderers/ExperimentRenderer.tsx +2 -2
  142. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -2
  143. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +2 -2
  144. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +4 -4
  145. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +11 -24
  146. package/src/components/resources/renderers/GatewayRouteReferences.test.tsx +63 -0
  147. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +3 -3
  148. package/src/components/resources/renderers/HPARenderer.tsx +57 -17
  149. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +12 -25
  150. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +3 -3
  151. package/src/components/resources/renderers/IngressClassReferences.test.tsx +52 -0
  152. package/src/components/resources/renderers/IngressClassRenderer.tsx +10 -3
  153. package/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx +33 -0
  154. package/src/components/resources/renderers/IngressRenderer.tsx +36 -5
  155. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +9 -9
  156. package/src/components/resources/renderers/IstioExactReferenceNavigation.test.tsx +34 -0
  157. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +5 -8
  158. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -14
  159. package/src/components/resources/renderers/KedaCredentials.test.tsx +85 -0
  160. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +15 -13
  161. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +6 -3
  162. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +24 -31
  163. package/src/components/resources/renderers/KnativeExactReferenceNavigation.test.tsx +47 -0
  164. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +6 -7
  165. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +7 -9
  166. package/src/components/resources/renderers/KnativeReferenceLink.tsx +14 -0
  167. package/src/components/resources/renderers/KnativeReferenceLinks.test.tsx +60 -0
  168. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +9 -11
  169. package/src/components/resources/renderers/KustomizationRenderer.tsx +2 -2
  170. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +3 -2
  171. package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +4 -3
  172. package/src/components/resources/renderers/LeaseRenderer.tsx +3 -15
  173. package/src/components/resources/renderers/ManagedResourceRenderer.tsx +2 -2
  174. package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +3 -2
  175. package/src/components/resources/renderers/NamespaceRenderer.test.ts +69 -2
  176. package/src/components/resources/renderers/NamespaceRenderer.tsx +6 -1
  177. package/src/components/resources/renderers/NodeRenderer.test.tsx +81 -1
  178. package/src/components/resources/renderers/NodeRenderer.tsx +56 -15
  179. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +3 -3
  180. package/src/components/resources/renderers/PodClassReferences.test.tsx +42 -0
  181. package/src/components/resources/renderers/PodNomination.test.tsx +57 -0
  182. package/src/components/resources/renderers/PodRenderer.test.tsx +32 -0
  183. package/src/components/resources/renderers/PodRenderer.tsx +62 -36
  184. package/src/components/resources/renderers/RolloutRenderer.test.tsx +1 -1
  185. package/src/components/resources/renderers/RolloutRenderer.tsx +3 -2
  186. package/src/components/resources/renderers/SbomReportRenderer.tsx +2 -2
  187. package/src/components/resources/renderers/SecretRenderer.test.tsx +88 -0
  188. package/src/components/resources/renderers/SecretRenderer.tsx +34 -5
  189. package/src/components/resources/renderers/ServiceRenderer.test.tsx +43 -0
  190. package/src/components/resources/renderers/ServiceRenderer.tsx +11 -3
  191. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +9 -22
  192. package/src/components/resources/renderers/VPARenderer.tsx +17 -11
  193. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +5 -7
  194. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +3 -3
  195. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +2 -2
  196. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +3 -3
  197. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +2 -2
  198. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +2 -2
  199. package/src/components/resources/renderers/WorkflowRenderer.tsx +4 -3
  200. package/src/components/resources/renderers/kyverno-modern-cells.tsx +1 -1
  201. package/src/components/resources/renderers/rollout/ReplicaSetProgression.tsx +2 -2
  202. package/src/components/resources/resource-utils-capi.ts +1 -1
  203. package/src/components/resources/resource-utils-cnpg.test.ts +35 -10
  204. package/src/components/resources/resource-utils-cnpg.ts +51 -24
  205. package/src/components/resources/resource-utils-contour.test.ts +44 -0
  206. package/src/components/resources/resource-utils-contour.ts +16 -0
  207. package/src/components/resources/resource-utils-istio.ts +12 -8
  208. package/src/components/resources/resource-utils-keda.ts +1 -7
  209. package/src/components/resources/resource-utils-knative.ts +18 -0
  210. package/src/components/resources/resource-utils-kyverno.ts +1 -7
  211. package/src/components/resources/resource-utils-velero.ts +0 -12
  212. package/src/components/resources/resource-utils.ts +99 -39
  213. package/src/components/resources/rules-column-width.test.ts +1 -1
  214. package/src/components/resources/summarize-scheduler-message.test.ts +30 -0
  215. package/src/components/shared/ActionConfirmDialog.test.tsx +154 -0
  216. package/src/components/shared/ActionConfirmDialog.tsx +246 -0
  217. package/src/components/shared/CreateResourceDialog.test.tsx +330 -0
  218. package/src/components/shared/CreateResourceDialog.tsx +126 -80
  219. package/src/components/shared/DetailShell.test.tsx +80 -0
  220. package/src/components/shared/DetailShell.tsx +85 -10
  221. package/src/components/shared/DrainPlanDialog.tsx +1 -0
  222. package/src/components/shared/EditableYamlView.test.tsx +7 -0
  223. package/src/components/shared/EditableYamlView.tsx +1 -1
  224. package/src/components/shared/GitOpsWriteWarning.test.tsx +86 -0
  225. package/src/components/shared/GitOpsWriteWarning.tsx +135 -0
  226. package/src/components/shared/ResourceActionsBar.tsx +20 -24
  227. package/src/components/shared/ResourceRendererDispatch.tsx +13 -10
  228. package/src/components/shared/SetImageDialog.test.ts +75 -6
  229. package/src/components/shared/SetImageDialog.tsx +92 -84
  230. package/src/components/shared/index.ts +4 -1
  231. package/src/components/timeline/TimelineList.tsx +2 -15
  232. package/src/components/timeline/TimelineSwimlanes.tsx +3 -11
  233. package/src/components/timeline/timeline-filters.ts +1 -0
  234. package/src/components/topology/K8sResourceNode.test.ts +18 -1
  235. package/src/components/topology/K8sResourceNode.tsx +44 -2
  236. package/src/components/topology/TopologyFilterSidebar.tsx +1 -0
  237. package/src/components/topology/TopologyGraph.tsx +8 -74
  238. package/src/components/topology/TopologySearch.tsx +1 -0
  239. package/src/components/topology/internet-groups.test.ts +35 -0
  240. package/src/components/topology/internet-groups.ts +55 -0
  241. package/src/components/topology/layout.ts +1 -1
  242. package/src/components/ui/Badge.tsx +1 -0
  243. package/src/components/ui/FoldSection.tsx +68 -0
  244. package/src/components/ui/ForceDeleteConfirmDialog.test.tsx +62 -0
  245. package/src/components/ui/ForceDeleteConfirmDialog.tsx +24 -12
  246. package/src/components/ui/RefLink.tsx +20 -0
  247. package/src/components/ui/RowActionMenu.tsx +8 -3
  248. package/src/components/ui/ScheduleValue.test.tsx +5 -0
  249. package/src/components/ui/ScheduleValue.tsx +3 -2
  250. package/src/components/ui/crd-definition-navigation.test.tsx +160 -0
  251. package/src/components/ui/drawer-components.test.tsx +175 -1
  252. package/src/components/ui/drawer-components.tsx +103 -36
  253. package/src/components/ui/index.ts +3 -1
  254. package/src/components/ui/monacoEditorFeatures.ts +77 -0
  255. package/src/components/ui/monacoRuntime.ts +14 -19
  256. package/src/components/ui/status-tone.test.ts +10 -1
  257. package/src/components/ui/status-tone.tsx +29 -0
  258. package/src/components/workload/WorkloadView.deleted.test.tsx +101 -0
  259. package/src/components/workload/WorkloadView.deletedDialog.test.tsx +45 -0
  260. package/src/components/workload/WorkloadView.relationships.test.tsx +37 -0
  261. package/src/components/workload/WorkloadView.secretSave.test.tsx +62 -0
  262. package/src/components/workload/WorkloadView.spec.test.tsx +149 -0
  263. package/src/components/workload/WorkloadView.tsx +297 -104
  264. package/src/index.ts +14 -1
  265. package/src/monaco-deep.d.ts +19 -12
  266. package/src/theme/components.css +13 -0
  267. package/src/types/capacity.ts +21 -2
  268. package/src/types/core.ts +60 -2
  269. package/src/types/gitops-flux-status.test.ts +32 -1
  270. package/src/types/gitops-insights.ts +19 -2
  271. package/src/types/gitops-tree.ts +10 -0
  272. package/src/types/gitops.ts +26 -6
  273. package/src/utils/api-resources.ts +7 -0
  274. package/src/utils/application-history.ts +1 -0
  275. package/src/utils/cnpg-backup.test.ts +26 -0
  276. package/src/utils/cnpg-backup.ts +70 -0
  277. package/src/utils/format.test.ts +49 -2
  278. package/src/utils/format.ts +9 -1
  279. package/src/utils/gateway-references.test.ts +17 -0
  280. package/src/utils/gateway-references.ts +29 -0
  281. package/src/utils/gitops-owner.ts +3 -2
  282. package/src/utils/gitops-write-guard.test.ts +401 -0
  283. package/src/utils/gitops-write-guard.ts +509 -0
  284. package/src/utils/grant.test.ts +19 -0
  285. package/src/utils/grant.ts +35 -0
  286. package/src/utils/index.ts +6 -0
  287. package/src/utils/ingress-class-references.ts +22 -0
  288. package/src/utils/k8s-errors.ts +7 -7
  289. package/src/utils/log-format.test.ts +41 -0
  290. package/src/utils/log-format.ts +71 -15
  291. package/src/utils/log-level.ts +12 -8
  292. package/src/utils/navigation.test.ts +70 -0
  293. package/src/utils/navigation.ts +91 -8
  294. package/src/utils/node-lifecycle.test.ts +36 -0
  295. package/src/utils/node-lifecycle.ts +77 -0
  296. package/src/utils/object-reference-navigation.test.ts +43 -0
  297. package/src/utils/resource-hierarchy.test.ts +69 -0
  298. package/src/utils/resource-hierarchy.ts +41 -7
  299. package/src/utils/resource-icons.ts +2 -0
  300. package/src/utils/resource-refs.test.ts +23 -0
  301. package/src/utils/resource-refs.ts +30 -0
  302. package/src/utils/skeleton-yaml.test.ts +21 -0
  303. package/src/utils/skeleton-yaml.ts +16 -0
  304. package/src/utils/url-path.test.ts +35 -0
  305. package/src/utils/url-path.ts +21 -0
  306. package/src/utils/validators.test.ts +4 -13
  307. package/src/utils/validators.ts +5 -19
  308. package/src/utils/view-transition.test.ts +35 -1
  309. package/src/utils/view-transition.ts +16 -1
@@ -14,6 +14,7 @@ import {
14
14
  getCNPGWALArchivingFailure,
15
15
  getCNPGLastBackupFailure,
16
16
  classifyCNPGClusterPhase,
17
+ cnpgBlockedPhaseExplanation,
17
18
  classifyCNPGBackupPhase,
18
19
  CNPG_CLUSTER_PHASES_HEALTHY,
19
20
  CNPG_CLUSTER_PHASES_TRANSIENT,
@@ -115,6 +116,26 @@ describe('getCNPGClusterCertificateExpirations', () => {
115
116
 
116
117
  const cluster = (status: any = {}, spec: any = {}) => ({ spec, status })
117
118
 
119
+ describe('cnpgBlockedPhaseExplanation', () => {
120
+ it('says the operator retries a plugin phase, and quotes its reason', () => {
121
+ const e = cnpgBlockedPhaseExplanation(
122
+ 'Cluster cannot proceed to reconciliation due to an unknown plugin being required',
123
+ "Unknown plugin: 'barman-cloud.cloudnative-pg.io'. Check the operator and plugin logs for errors",
124
+ )
125
+ expect(e.title).toBe('Reconciliation is blocked by a plugin')
126
+ expect(e.body).toMatch(/retries every few seconds/)
127
+ expect(e.body).not.toMatch(/manual intervention|does not resolve/)
128
+ expect(e.body).toMatch(/The operator reports: Unknown plugin/)
129
+ expect(e.body.endsWith('for errors.')).toBe(true)
130
+ expect(e.message.startsWith('Cluster cannot proceed')).toBe(true)
131
+ })
132
+ it('keeps manual intervention for the one phase upstream says needs it', () => {
133
+ expect(cnpgBlockedPhaseExplanation('Cluster is unrecoverable and needs manual intervention').title).toBe('Cluster is unrecoverable')
134
+ const objects = cnpgBlockedPhaseExplanation('Unable to create required cluster objects', ' ')
135
+ expect(objects.body).toBe('The operator keeps retrying, but this does not clear until its cause is fixed.')
136
+ })
137
+ })
138
+
118
139
  describe('classifyCNPGClusterPhase', () => {
119
140
  it('buckets every phase constant shipped by CNPG 1.27', () => {
120
141
  for (const p of CNPG_CLUSTER_PHASES_HEALTHY) expect(classifyCNPGClusterPhase(p)).toBe('healthy')
@@ -365,13 +386,17 @@ describe('getCNPGClusterInstancesReportedState', () => {
365
386
  // ============================================================================
366
387
 
367
388
  describe('getCNPGPoolerStatus', () => {
389
+ it('uses the requested count during scale-down without claiming readiness', () => {
390
+ expect(getCNPGPoolerStatus({ spec: { instances: 2 }, status: { instances: 3 } })).toMatchObject({ text: '2 instances requested', level: 'neutral' })
391
+ expect(getCNPGPoolerStatus({ spec: { instances: 1 }, status: { instances: 1 } })).toMatchObject({ text: '1 instance requested', level: 'neutral' })
392
+ })
368
393
  it('does not claim readiness from status.instances, which counts scheduled pods', () => {
369
394
  // Verified live: a Pooler whose 2 PgBouncer pods are both Pending (0/1
370
395
  // ready) still reports status.instances=2. Calling that "Ready" renders a
371
396
  // broken Pooler green.
372
397
  const s = getCNPGPoolerStatus({ spec: { instances: 2 }, status: { instances: 2 } })
373
- expect(s.level).toBe('healthy')
374
- expect(s.text).toBe('Scheduled')
398
+ expect(s.level).toBe('neutral')
399
+ expect(s.text).toBe('2 instances requested')
375
400
  expect(s.text).not.toBe('Ready')
376
401
  })
377
402
 
@@ -595,7 +620,7 @@ describe('status column filter reads the badge, not the raw phase', () => {
595
620
  )).toBe('Suspended')
596
621
  expect(getCellFilterValue(
597
622
  { apiVersion: 'postgresql.cnpg.io/v1', spec: { instances: 2 }, status: { instances: 2 } }, 'status', 'poolers',
598
- )).toBe('Scheduled')
623
+ )).toBe('2 instances requested')
599
624
  })
600
625
 
601
626
  it('leaves a foreign CRD sharing the plural on the generic path', () => {
@@ -670,7 +695,7 @@ describe('an absent count is unknown, never zero', () => {
670
695
  expect(getCNPGPoolerStatus({ spec: { instances: 2 }, status: { instances: 0 } }))
671
696
  .toMatchObject({ text: 'Not Scheduled', level: 'unhealthy' })
672
697
  expect(getCNPGPoolerStatus({ spec: { instances: 2 }, status: { instances: 2 } }))
673
- .toMatchObject({ text: 'Scheduled', level: 'healthy' })
698
+ .toMatchObject({ text: '2 instances requested', level: 'neutral' })
674
699
  })
675
700
  })
676
701
 
@@ -696,9 +721,9 @@ describe('paused Pooler', () => {
696
721
 
697
722
  it('only an explicit true pauses', () => {
698
723
  expect(getCNPGPoolerStatus({ spec: { instances: 2, pgbouncer: {} }, status: { instances: 2 } }))
699
- .toMatchObject({ text: 'Scheduled', level: 'healthy' })
724
+ .toMatchObject({ text: '2 instances requested', level: 'neutral' })
700
725
  expect(getCNPGPoolerStatus({ spec: { instances: 2, pgbouncer: { paused: false } }, status: { instances: 2 } }))
701
- .toMatchObject({ text: 'Scheduled', level: 'healthy' })
726
+ .toMatchObject({ text: '2 instances requested', level: 'neutral' })
702
727
  expect(isCNPGPoolerPaused({ spec: { pgbouncer: { paused: true } } })).toBe(true)
703
728
  expect(isCNPGPoolerPaused({ spec: {} })).toBe(false)
704
729
  })
@@ -752,9 +777,9 @@ describe('CNPG scheduled backup lateness', () => {
752
777
  expect(badge.text).toBe('Overdue')
753
778
  })
754
779
 
755
- it('stays healthy inside the operator reconcile window', () => {
780
+ it('stays neutral inside the operator reconcile window', () => {
756
781
  const badge = getCNPGScheduledBackupStatus(sb(false, at(-1 * MIN), at(-24 * 60 * MIN)))
757
- expect(badge.level).toBe('healthy')
782
+ expect(badge.level).toBe('neutral')
758
783
  })
759
784
 
760
785
  it('keeps suspended as a deliberate state, not a missed backup', () => {
@@ -768,7 +793,7 @@ describe('CNPG scheduled backup lateness', () => {
768
793
  // The two read from one test. Before this they disagreed: the badge said
769
794
  // Active while this field said overdue on the same row.
770
795
  const justPastDue = sb(false, at(-1 * MIN), at(-24 * 60 * MIN))
771
- expect(getCNPGScheduledBackupStatus(justPastDue).level).toBe('healthy')
796
+ expect(getCNPGScheduledBackupStatus(justPastDue).level).toBe('neutral')
772
797
  expect(getCNPGScheduledBackupNextSchedule(justPastDue)).toBe('due now')
773
798
 
774
799
  const longPastDue = sb(false, at(-200 * 24 * 60 * MIN), at(-200 * 24 * 60 * MIN))
@@ -783,7 +808,7 @@ describe('CNPG scheduled backup lateness', () => {
783
808
 
784
809
  it('says nothing when the operator published no next-run time', () => {
785
810
  expect(getCNPGScheduledBackupNextSchedule(sb(false))).toBe('-')
786
- expect(getCNPGScheduledBackupStatus(sb(false, undefined, at(-24 * 60 * MIN))).level).toBe('healthy')
811
+ expect(getCNPGScheduledBackupStatus(sb(false, undefined, at(-24 * 60 * MIN))).level).toBe('neutral')
787
812
  })
788
813
  })
789
814
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  import type { StatusBadge } from './resource-utils'
4
4
  import { healthColors, formatAge, formatDuration } from './resource-utils'
5
+ import { cnpgBackupDeclaration, cnpgBarmanPlugin } from '../../utils/cnpg-backup'
6
+ export { CNPG_BARMAN_PLUGIN_NAME } from '../../utils/cnpg-backup'
7
+
5
8
  import { parseGoTimeString } from '../../utils/parse-go-time'
6
9
 
7
10
  // ============================================================================
@@ -10,6 +13,11 @@ import { parseGoTimeString } from '../../utils/parse-go-time'
10
13
 
11
14
  export const CNPG_GROUP = 'postgresql.cnpg.io'
12
15
 
16
+ export function getCNPGPostgresMajor(cluster: any): number | undefined {
17
+ const major = cluster?.status?.pgDataImageInfo?.majorVersion ?? cluster?.spec?.imageCatalogRef?.major
18
+ return typeof major === 'number' && Number.isInteger(major) && major > 0 ? major : undefined
19
+ }
20
+
13
21
  /**
14
22
  * Exact API-group match for a resource's `apiVersion` ("group/version").
15
23
  *
@@ -56,7 +64,11 @@ export const CNPG_CLUSTER_PHASES_TRANSIENT = [
56
64
  /** Losing the primary — degraded now, self-resolving only if a replica can take over. */
57
65
  export const CNPG_CLUSTER_PHASES_FAILING = ['Failing over'] as const
58
66
 
59
- /** Reconciliation has stopped. These need a human; none of them self-heal. */
67
+ /**
68
+ * Reconciliation is blocked. The operator keeps retrying every one of these;
69
+ * only the plugin phases can clear without anyone changing something (a plugin
70
+ * Pod that comes back). See cnpgBlockedPhaseExplanation.
71
+ */
60
72
  export const CNPG_CLUSTER_PHASES_TERMINAL = [
61
73
  'Cluster is unrecoverable and needs manual intervention',
62
74
  // Not in 1.27 or 1.28; added upstream after 1.28 (PhaseDefinitionInvalid).
@@ -135,6 +147,34 @@ export function getCNPGClusterDisplayState(phase: string): string {
135
147
 
136
148
  export type CNPGPhaseBucket = 'healthy' | 'transient' | 'failing' | 'terminal' | 'attention' | 'unknown'
137
149
 
150
+ const CNPG_PLUGIN_BLOCKED_PHASES = new Set<string>([
151
+ 'Cluster cannot proceed to reconciliation due to an unknown plugin being required',
152
+ 'Cluster cannot proceed to reconciliation due to an error while interacting with plugins',
153
+ ])
154
+
155
+ /**
156
+ * What a blocked phase means for whoever is on call, with the operator's own
157
+ * `status.phaseReason` when it set one. The operator requeues the plugin
158
+ * phases every 10–15 s, so they clear once the plugin loads and answers;
159
+ * the others keep failing until their cause is fixed; "unrecoverable" is the
160
+ * one upstream itself says needs manual intervention.
161
+ */
162
+ export function cnpgBlockedPhaseExplanation(phase: string, phaseReason?: string): { title: string; message: string; body: string } {
163
+ const said = typeof phaseReason === 'string' ? phaseReason.trim() : ''
164
+ const reason = said ? ` The operator reports: ${/[.!?]$/.test(said) ? said : `${said}.`}` : ''
165
+ const out = (title: string, body: string) => ({ title, body: `${body}${reason}`, message: `${phase}. ${body}${reason}` })
166
+ if (phase === 'Cluster is unrecoverable and needs manual intervention') {
167
+ return out('Cluster is unrecoverable', 'The operator cannot bring it back by itself.')
168
+ }
169
+ if (CNPG_PLUGIN_BLOCKED_PHASES.has(phase)) {
170
+ return out(
171
+ 'Reconciliation is blocked by a plugin',
172
+ 'The operator retries every few seconds and continues once the plugin is loaded and answering, so a plugin Pod that is restarting clears on its own; check the operator and plugin logs if it does not.',
173
+ )
174
+ }
175
+ return out('Reconciliation is blocked', 'The operator keeps retrying, but this does not clear until its cause is fixed.')
176
+ }
177
+
138
178
  export function classifyCNPGClusterPhase(phase: string): CNPGPhaseBucket {
139
179
  if (!phase) return 'unknown'
140
180
  if ((CNPG_CLUSTER_PHASES_HEALTHY as readonly string[]).includes(phase)) return 'healthy'
@@ -414,7 +454,6 @@ export function getCNPGClusterUpdateStrategy(resource: any): string {
414
454
  return resource.spec?.primaryUpdateStrategy || 'unsupervised'
415
455
  }
416
456
 
417
- export const CNPG_BARMAN_PLUGIN_NAME = 'barman-cloud.cloudnative-pg.io'
418
457
 
419
458
  /** The API group ObjectStore is served under. `objectstores` is a generic
420
459
  * plural several operators ship, so every link to one has to be qualified —
@@ -438,20 +477,10 @@ export interface CNPGBarmanPlugin {
438
477
  * status.lastSuccessfulBackup / firstRecoverabilityPoint entirely.
439
478
  */
440
479
  export function getCNPGClusterBarmanPlugin(resource: any): CNPGBarmanPlugin | null {
441
- const plugins = resource.spec?.plugins
442
- if (!Array.isArray(plugins)) return null
443
- const plugin = plugins.find(
444
- (p: any) => p?.name === CNPG_BARMAN_PLUGIN_NAME && p?.enabled !== false
445
- )
480
+ const plugin = cnpgBarmanPlugin(cnpgBackupDeclaration(resource))
446
481
  if (!plugin) return null
447
- return {
448
- name: plugin.name,
449
- barmanObjectName: plugin.parameters?.barmanObjectName,
450
- // ObjectStore keys the recovery window by the plugin's serverName when set,
451
- // otherwise by the cluster name.
452
- serverName: plugin.parameters?.serverName || resource.metadata?.name,
453
- isWALArchiver: plugin.isWALArchiver === true,
454
- }
482
+ const { name, barmanObjectName, serverName, isWALArchiver } = plugin
483
+ return { name, barmanObjectName, serverName, isWALArchiver }
455
484
  }
456
485
 
457
486
  export function getCNPGClusterBackupConfig(resource: any): {
@@ -518,7 +547,7 @@ export function getCNPGDeclarativeMessage(resource: any): string | undefined {
518
547
  export function getCNPGReclaimPolicy(resource: any): { value: string; destructive: boolean } {
519
548
  const spec = resource?.spec ?? {}
520
549
  const raw =
521
- spec.databaseReclaimPolicy ?? spec.publicationReclaimPolicy ?? spec.subscriptionReclaimPolicy ?? 'retain'
550
+ spec.databaseReclaimPolicy ?? spec.publicationReclaimPolicy ?? spec.subscriptionReclaimPolicy ?? spec.databaseRoleReclaimPolicy ?? 'retain'
522
551
  return { value: raw, destructive: String(raw).toLowerCase() === 'delete' }
523
552
  }
524
553
 
@@ -865,10 +894,6 @@ export function getCNPGBackupStartedAt(resource: any): string {
865
894
  return resource.status?.startedAt ? formatAge(resource.status.startedAt) : '-'
866
895
  }
867
896
 
868
- export function getCNPGBackupStoppedAt(resource: any): string {
869
- return resource.status?.stoppedAt ? formatAge(resource.status.stoppedAt) : '-'
870
- }
871
-
872
897
  export function getCNPGBackupName(resource: any): string {
873
898
  return resource.status?.backupName || '-'
874
899
  }
@@ -937,15 +962,15 @@ export function getCNPGScheduledBackupStatus(resource: any): StatusBadge {
937
962
 
938
963
  // If we have a last schedule time, it's active
939
964
  if (resource.status?.lastScheduleTime) {
940
- return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
965
+ return { text: 'Enabled', color: healthColors.neutral, level: 'neutral' }
941
966
  }
942
967
 
943
968
  // Check if immediate flag is set and no schedule has run yet
944
969
  if (resource.spec?.immediate) {
945
- return { text: 'Immediate', color: healthColors.healthy, level: 'healthy' }
970
+ return { text: 'Immediate run requested', color: healthColors.neutral, level: 'neutral' }
946
971
  }
947
972
 
948
- return { text: 'Scheduled', color: healthColors.healthy, level: 'healthy' }
973
+ return { text: 'Enabled · not run yet', color: healthColors.neutral, level: 'neutral' }
949
974
  }
950
975
 
951
976
  export function getCNPGScheduledBackupCluster(resource: any): string {
@@ -1034,8 +1059,10 @@ export function getCNPGPoolerStatus(resource: any): StatusBadge {
1034
1059
  return { text: 'Paused', color: healthColors.degraded, level: 'degraded' }
1035
1060
  }
1036
1061
 
1062
+ // status.instances counts scheduled Pods only; readiness lives on the
1063
+ // Pooler's Deployment, so this badge must not read as healthy.
1037
1064
  if (desired > 0 && scheduled >= desired) {
1038
- return { text: 'Scheduled', color: healthColors.healthy, level: 'healthy' }
1065
+ return { text: `${desired} instance${desired === 1 ? '' : 's'} requested`, color: healthColors.neutral, level: 'neutral' }
1039
1066
  }
1040
1067
 
1041
1068
  return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { readFileSync } from 'node:fs'
3
+ import { getContourSecretReference, getHTTPProxyTLSSecret } from './resource-utils-contour'
4
+
5
+ const proxy = (secretName?: string) => ({
6
+ metadata: { namespace: 'shop' },
7
+ spec: { virtualhost: { fqdn: 'shop.example.com', tls: secretName === undefined ? {} : { secretName } } },
8
+ })
9
+
10
+ describe('getHTTPProxyTLSSecret', () => {
11
+ it('resolves a bare name in the proxy namespace', () => {
12
+ expect(getHTTPProxyTLSSecret(proxy('cert'))).toEqual({ namespace: 'shop', name: 'cert' })
13
+ })
14
+ it('resolves a delegated namespace/name', () => {
15
+ expect(getHTTPProxyTLSSecret(proxy('certs/wildcard'))).toEqual({ namespace: 'certs', name: 'wildcard' })
16
+ })
17
+ it('defaults an empty namespace to the proxy namespace', () => {
18
+ expect(getHTTPProxyTLSSecret(proxy('/cert'))).toEqual({ namespace: 'shop', name: 'cert' })
19
+ })
20
+ it('rejects names that cannot identify a Kubernetes Secret', () => {
21
+ expect(getHTTPProxyTLSSecret(proxy('certs/a/b'))).toBeNull()
22
+ })
23
+ it('returns null without a secret', () => {
24
+ expect(getHTTPProxyTLSSecret(proxy())).toBeNull()
25
+ })
26
+ })
27
+
28
+ interface ReferenceCase {
29
+ case: string
30
+ raw: string
31
+ defaultNamespace: string
32
+ valid: boolean
33
+ namespace?: string
34
+ name?: string
35
+ }
36
+ const referenceCases: ReferenceCase[] = JSON.parse(readFileSync(new URL('../../../../../pkg/configrefs/testdata/contour_secret_refs.json', import.meta.url), 'utf8'))
37
+
38
+ describe('shared Go/TS Contour reference contract', () => {
39
+ it.each(referenceCases)('$case', (tc) => {
40
+ const expected = tc.valid ? { namespace: tc.namespace, name: tc.name } : undefined
41
+ expect(getContourSecretReference(tc.raw, tc.defaultNamespace)).toEqual(expected)
42
+ expect(getHTTPProxyTLSSecret({ ...proxy(tc.raw), metadata: { namespace: tc.defaultNamespace } })).toEqual(expected ?? null)
43
+ })
44
+ })
@@ -1,3 +1,5 @@
1
+ import { validateRFC1123Label, validateRFC1123Subdomain } from '../../utils/validators'
2
+
1
3
  // Contour HTTPProxy CRD utility functions for resource list cells and detail renderers
2
4
 
3
5
  export function getHTTPProxyFQDN(resource: any): string {
@@ -32,3 +34,17 @@ export function getHTTPProxyIncludeCount(resource: any): number {
32
34
  export function hasHTTPProxyTLS(resource: any): boolean {
33
35
  return !!resource.spec?.virtualhost?.tls
34
36
  }
37
+
38
+ export function getHTTPProxyTLSSecret(resource: any): { namespace: string; name: string } | null {
39
+ const raw: string | undefined = resource.spec?.virtualhost?.tls?.secretName
40
+ return raw ? getContourSecretReference(raw, resource.metadata?.namespace ?? '') ?? null : null
41
+ }
42
+
43
+ /** Contour's delegated TLS secret form is namespace/name. */
44
+ export function getContourSecretReference(raw: string, defaultNamespace: string): { name: string; namespace: string } | undefined {
45
+ const parts = raw.split('/')
46
+ const namespace = parts.length === 1 ? defaultNamespace : parts[0] || defaultNamespace
47
+ const name = parts.length === 1 ? parts[0] : parts[1]
48
+ if (parts.length > 2 || !validateRFC1123Label(namespace).valid || !validateRFC1123Subdomain(name).valid) return undefined
49
+ return { name, namespace }
50
+ }
@@ -8,13 +8,17 @@ import { pluralize } from '../../utils/pluralize'
8
8
  // SHARED HELPERS
9
9
  // ============================================================================
10
10
 
11
- function parseIstioHost(host: string): { name: string; namespace: string } {
12
- // Istio hosts can be: "reviews", "reviews.default", "reviews.default.svc.cluster.local"
11
+ // FQDNs require the registry domain supplied by the producer. Undotted
12
+ // names use Istio's documented namespace-relative expansion.
13
+ export function resolveIstioServiceHost(host: string, defaultNamespace: string, serviceDomain = ''): { name: string; namespace: string } | undefined {
14
+ const label = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/
13
15
  const parts = host.split('.')
14
- return {
15
- name: parts[0] || host,
16
- namespace: parts.length >= 2 ? parts[1] : '',
16
+ if (parts.length === 1 && host.length <= 63 && label.test(host)) return { name: host, namespace: defaultNamespace }
17
+ if (serviceDomain && host.endsWith(`.svc.${serviceDomain}`)) {
18
+ const service = host.slice(0, -(`.svc.${serviceDomain}`.length)).split('.')
19
+ if (service.length === 2 && service.every(part => part.length <= 63 && label.test(part))) return { name: service[0], namespace: service[1] }
17
20
  }
21
+ return undefined
18
22
  }
19
23
 
20
24
  // ============================================================================
@@ -103,16 +107,16 @@ export function getVirtualServiceTlsRoutes(resource: any): any[] {
103
107
  return resource.spec?.tls || []
104
108
  }
105
109
 
106
- export function getVirtualServiceDestinations(resource: any): Array<{ host: string; namespace: string; port?: number; subset?: string; weight?: number }> {
110
+ export function getVirtualServiceDestinations(resource: any, serviceDomain = ''): Array<{ host: string; namespace: string; port?: number; subset?: string; weight?: number }> {
107
111
  const destinations: Array<{ host: string; namespace: string; port?: number; subset?: string; weight?: number }> = []
108
112
  const httpRoutes = resource.spec?.http || []
109
113
  for (const route of httpRoutes) {
110
114
  for (const dest of (route.route || [])) {
111
115
  if (dest.destination?.host) {
112
- const parsed = parseIstioHost(dest.destination.host)
116
+ const parsed = resolveIstioServiceHost(dest.destination.host, resource.metadata?.namespace || '', serviceDomain)
113
117
  destinations.push({
114
118
  host: dest.destination.host,
115
- namespace: parsed.namespace || resource.metadata?.namespace || '',
119
+ namespace: parsed?.namespace || '',
116
120
  port: dest.destination.port?.number,
117
121
  subset: dest.destination.subset,
118
122
  weight: dest.weight,
@@ -1,7 +1,7 @@
1
1
  // KEDA CRD utility functions
2
2
 
3
3
  import type { StatusBadge } from './resource-utils'
4
- import { healthColors, formatAge } from './resource-utils'
4
+ import { healthColors } from './resource-utils'
5
5
 
6
6
  // ============================================================================
7
7
  // SHARED HELPERS
@@ -108,12 +108,6 @@ export function getScaledObjectHpaName(resource: any): string {
108
108
  return resource.status?.hpaName || '-'
109
109
  }
110
110
 
111
- export function getScaledObjectLastActiveTime(resource: any): string {
112
- const lastActive = resource.status?.lastActiveTime
113
- if (!lastActive) return '-'
114
- return formatAge(lastActive)
115
- }
116
-
117
111
  export function getScaledObjectPollingInterval(resource: any): number {
118
112
  return resource.spec?.pollingInterval ?? 30
119
113
  }
@@ -1,3 +1,21 @@
1
+ import { apiVersionToGroup, kindToPluralWithGroup } from '../../utils/navigation'
2
+ import type { ResourceRef } from '../../types/core'
3
+
4
+ /** KReference records apiVersion or an explicit nonempty group. */
5
+ export function getKnativeReference(reference: any, defaultNamespace: string): ResourceRef | undefined {
6
+ const version = reference?.apiVersion
7
+ let group = reference?.group
8
+ if (typeof version === 'string' && version) {
9
+ const parts = version.split('/')
10
+ if (parts.length > 2 || parts.some(part => !part)) return undefined
11
+ const observed = apiVersionToGroup(version)
12
+ if (group && group !== observed) return undefined
13
+ group = observed
14
+ } else if (typeof group !== 'string' || !group) return undefined
15
+ if (typeof reference?.kind !== 'string' || !reference.kind || typeof reference?.name !== 'string' || !reference.name) return undefined
16
+ return { group, kind: kindToPluralWithGroup(reference.kind, group), name: reference.name, namespace: reference.namespace || defaultNamespace }
17
+ }
18
+
1
19
  // KNative CRD utility functions
2
20
 
3
21
  import type { StatusBadge } from './resource-utils'
@@ -1,7 +1,7 @@
1
1
  // Kyverno / Policy Report CRD utility functions
2
2
 
3
3
  import type { StatusBadge } from './resource-utils'
4
- import { healthColors, formatAge } from './resource-utils'
4
+ import { healthColors } from './resource-utils'
5
5
 
6
6
  // ============================================================================
7
7
  // SHARED HELPERS
@@ -286,12 +286,6 @@ export function getKyvernoPolicyAutogenRules(resource: any): string[] {
286
286
  return autogen.map((r: any) => r.name).filter(Boolean)
287
287
  }
288
288
 
289
- export function getKyvernoPolicyLastScheduleTime(resource: any): string {
290
- const lastSchedule = resource.status?.lastScheduleTime
291
- if (!lastSchedule) return '-'
292
- return formatAge(lastSchedule)
293
- }
294
-
295
289
  // ============================================================================
296
290
  // KYVERNO CLUSTERPOLICY UTILITIES (kyverno.io/v1)
297
291
  // ============================================================================
@@ -504,12 +504,6 @@ export function getBSLLastValidation(resource: any): string {
504
504
  return formatAge(lastValidation)
505
505
  }
506
506
 
507
- export function getBSLLastSynced(resource: any): string {
508
- const lastSynced = resource.status?.lastSyncedTime
509
- if (!lastSynced) return '-'
510
- return formatAge(lastSynced)
511
- }
512
-
513
507
  // Why the location is unavailable, in the validation controller's own words.
514
508
  // A BackupStorageLocation carries no conditions, so status.message is the only
515
509
  // place the reason exists — without it the phase is the whole story a reader
@@ -571,12 +565,6 @@ export function getBackupRepositoryMaintenanceFrequency(resource: any): string {
571
565
  return resource.spec?.maintenanceFrequency || '-'
572
566
  }
573
567
 
574
- export function getBackupRepositoryLastMaintenance(resource: any): string {
575
- const at = resource.status?.lastMaintenanceTime
576
- if (!at) return '-'
577
- return formatAge(at)
578
- }
579
-
580
568
  // Why the repository is not ready, in the controller's own words. Velero writes
581
569
  // it on the object and nowhere else, so without it the phase is the whole story
582
570
  // a reader gets.