@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.
- package/README.md +41 -0
- package/package.json +3 -3
- package/src/components/applications/ApplicationDetail.test.tsx +15 -0
- package/src/components/applications/ApplicationDetail.tsx +17 -15
- package/src/components/charts/AreaChart.interaction.test.tsx +118 -0
- package/src/components/charts/AreaChart.test.tsx +25 -0
- package/src/components/charts/AreaChart.tsx +141 -12
- package/src/components/charts/index.ts +1 -0
- package/src/components/checks/ChecksView.tsx +18 -1
- package/src/components/checks/types.ts +2 -13
- package/src/components/cnpg/CNPGBackupSummary.tsx +57 -9
- package/src/components/cnpg/CNPGClusterHASection.test.tsx +90 -0
- package/src/components/cnpg/CNPGClusterHASection.tsx +331 -0
- package/src/components/cnpg/CNPGClusterSummary.test.tsx +275 -0
- package/src/components/cnpg/CNPGClusterSummary.tsx +310 -96
- package/src/components/cnpg/CNPGConnectSection.test.tsx +64 -0
- package/src/components/cnpg/CNPGConnectSection.tsx +178 -0
- package/src/components/cnpg/CNPGDeclarativeSummary.tsx +157 -26
- package/src/components/cnpg/CNPGImageCatalogSummary.tsx +7 -4
- package/src/components/cnpg/CNPGLogicalPath.test.tsx +22 -0
- package/src/components/cnpg/CNPGLogicalPath.tsx +150 -0
- package/src/components/cnpg/CNPGObjectStoreSummary.tsx +9 -6
- package/src/components/cnpg/CNPGObjectSummary.test.tsx +193 -2
- package/src/components/cnpg/CNPGPoolerScheduling.test.tsx +36 -0
- package/src/components/cnpg/CNPGPoolerSummary.tsx +241 -14
- package/src/components/cnpg/CNPGSharedSummary.tsx +15 -8
- package/src/components/cnpg/CNPGWALArchivingFact.test.tsx +39 -0
- package/src/components/cnpg/backupRuns.test.ts +17 -0
- package/src/components/cnpg/backupRuns.ts +19 -0
- package/src/components/cnpg/connect.test.ts +124 -0
- package/src/components/cnpg/connect.ts +155 -0
- package/src/components/cnpg/databaseRole.test.ts +38 -0
- package/src/components/cnpg/databaseRole.ts +80 -0
- package/src/components/cnpg/ha.test.ts +446 -0
- package/src/components/cnpg/ha.ts +621 -0
- package/src/components/cnpg/index.ts +18 -0
- package/src/components/cnpg/logicalReplication.test.ts +121 -0
- package/src/components/cnpg/logicalReplication.ts +252 -0
- package/src/components/cnpg/pooler.test.ts +84 -0
- package/src/components/cnpg/pooler.ts +225 -0
- package/src/components/cnpg/primitives.tsx +21 -117
- package/src/components/cnpg/problems.ts +12 -0
- package/src/components/cnpg/relations.test.ts +63 -13
- package/src/components/cnpg/relations.ts +61 -52
- package/src/components/cnpg/schedule.ts +35 -0
- package/src/components/cnpg/workspace-disk.test.ts +97 -0
- package/src/components/cnpg/workspace-fleet-metrics.test.ts +250 -0
- package/src/components/cnpg/workspace-problems.test.ts +175 -0
- package/src/components/cnpg/workspace.test.ts +380 -9
- package/src/components/cnpg/workspace.ts +1137 -136
- package/src/components/dock/DockContext.test.tsx +32 -0
- package/src/components/dock/DockContext.tsx +18 -4
- package/src/components/dock/LocalTerminalTab.test.tsx +40 -0
- package/src/components/dock/LocalTerminalTab.tsx +4 -3
- package/src/components/dock/TerminalTab.tsx +20 -8
- package/src/components/facts/certainty.tsx +36 -0
- package/src/components/facts/facts.test.tsx +24 -0
- package/src/components/facts/facts.tsx +62 -0
- package/src/components/facts/index.ts +6 -0
- package/src/components/facts/managed-by.test.tsx +17 -0
- package/src/components/facts/managed-by.tsx +36 -0
- package/src/components/facts/public-exports.test.tsx +35 -0
- package/src/components/gitops/GitOpsGraphFilterRail.tsx +6 -2
- package/src/components/gitops/GitOpsHealthSourceNotice.test.tsx +38 -0
- package/src/components/gitops/GitOpsHealthSourceNotice.tsx +13 -3
- package/src/components/gitops/GitOpsTableView.tsx +9 -24
- package/src/components/gitops/detail-helpers.ts +1 -1
- package/src/components/gitops/insights/ArgoResourceDiff.test.tsx +115 -0
- package/src/components/gitops/insights/ArgoResourceDiff.tsx +22 -5
- package/src/components/gitops/insights/DriftPanel.tsx +62 -0
- package/src/components/gitops/insights/GitOpsInsightViews.tsx +29 -83
- package/src/components/gitops/tree/GitOpsTreeGraph.test.ts +26 -1
- package/src/components/gitops/tree/GitOpsTreeGraph.tsx +40 -60
- package/src/components/gitops/tree/layout.test.ts +111 -0
- package/src/components/gitops/tree/layout.ts +128 -0
- package/src/components/gitops/tree/tree-helpers.test.ts +17 -0
- package/src/components/gitops/tree/tree-helpers.ts +7 -1
- package/src/components/issues/ConditionalCleanup.test.tsx +82 -0
- package/src/components/issues/IssuesView.tsx +40 -5
- package/src/components/issues/ResourceIssuesSection.test.tsx +28 -0
- package/src/components/issues/ResourceIssuesSection.tsx +3 -0
- package/src/components/issues/index.ts +3 -1
- package/src/components/issues/issue-timing.ts +14 -3
- package/src/components/issues/issues.test.ts +50 -2
- package/src/components/issues/severity.ts +21 -0
- package/src/components/issues/types.ts +23 -0
- package/src/components/logs/LogCore.theme.test.tsx +81 -3
- package/src/components/logs/LogCore.tsx +25 -17
- package/src/components/logs/LogToolbarSelects.tsx +3 -1
- package/src/components/logs/LogsViewer.test.tsx +291 -0
- package/src/components/logs/LogsViewer.tsx +72 -11
- package/src/components/logs/StructuredLogLine.test.tsx +76 -0
- package/src/components/logs/StructuredLogLine.tsx +22 -30
- package/src/components/logs/WorkloadLogsViewer.notice.test.tsx +59 -0
- package/src/components/logs/WorkloadLogsViewer.test.tsx +65 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +44 -10
- package/src/components/logs/useLogBuffer.test.ts +8 -0
- package/src/components/logs/useLogStream.ts +28 -1
- package/src/components/problems/index.ts +1 -0
- package/src/components/problems/problems.test.tsx +63 -0
- package/src/components/problems/problems.tsx +165 -0
- package/src/components/resources/ActiveFilterBar.test.tsx +78 -0
- package/src/components/resources/ActiveFilterBar.tsx +86 -0
- package/src/components/resources/ResourcesSidebar.keyboard.test.tsx +42 -0
- package/src/components/resources/ResourcesSidebar.test.tsx +36 -2
- package/src/components/resources/ResourcesSidebar.tsx +92 -41
- package/src/components/resources/ResourcesView.kind-filter-memory.test.ts +52 -0
- package/src/components/resources/ResourcesView.kind-view.test.tsx +149 -0
- package/src/components/resources/ResourcesView.per-container.test.ts +56 -1
- package/src/components/resources/ResourcesView.tsx +309 -117
- package/src/components/resources/curated-column-ownership.test.ts +3 -3
- package/src/components/resources/renderers/AnalysisRunRenderer.tsx +3 -3
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +6 -5
- package/src/components/resources/renderers/AutoscalingReferences.test.tsx +286 -0
- package/src/components/resources/renderers/CAPIClusterRenderer.tsx +5 -4
- package/src/components/resources/renderers/CAPIExactReferenceNavigation.test.tsx +27 -0
- package/src/components/resources/renderers/CAPIKubeadmControlPlaneRenderer.tsx +2 -2
- package/src/components/resources/renderers/CAPIMachineDeploymentRenderer.tsx +2 -2
- package/src/components/resources/renderers/CAPIMachinePoolRenderer.tsx +2 -2
- package/src/components/resources/renderers/CAPIMachineRenderer.tsx +2 -2
- package/src/components/resources/renderers/CAPIMachineSetRenderer.tsx +2 -2
- package/src/components/resources/renderers/CNPGBackupRenderer.test.tsx +23 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +8 -19
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +7 -8
- package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +25 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.test.tsx +32 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +9 -17
- package/src/components/resources/renderers/CNPGShared.tsx +2 -2
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/CompositeRenderer.navigation.test.tsx +35 -0
- package/src/components/resources/renderers/CompositeRenderer.tsx +3 -3
- package/src/components/resources/renderers/CompositionRenderer.tsx +3 -3
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/ContourExactReferenceNavigation.test.tsx +34 -0
- package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +11 -10
- package/src/components/resources/renderers/CronJobRenderer.tsx +6 -4
- package/src/components/resources/renderers/CronWorkflowRenderer.tsx +3 -2
- package/src/components/resources/renderers/EndpointSliceRenderer.test.tsx +62 -0
- package/src/components/resources/renderers/EndpointSliceRenderer.tsx +15 -14
- package/src/components/resources/renderers/EventRenderer.tsx +3 -3
- package/src/components/resources/renderers/ExperimentRenderer.tsx +2 -2
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +2 -2
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +4 -4
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +11 -24
- package/src/components/resources/renderers/GatewayRouteReferences.test.tsx +63 -0
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +3 -3
- package/src/components/resources/renderers/HPARenderer.tsx +57 -17
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +12 -25
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +3 -3
- package/src/components/resources/renderers/IngressClassReferences.test.tsx +52 -0
- package/src/components/resources/renderers/IngressClassRenderer.tsx +10 -3
- package/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx +33 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +36 -5
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +9 -9
- package/src/components/resources/renderers/IstioExactReferenceNavigation.test.tsx +34 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +5 -8
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -14
- package/src/components/resources/renderers/KedaCredentials.test.tsx +85 -0
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +15 -13
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +6 -3
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +24 -31
- package/src/components/resources/renderers/KnativeExactReferenceNavigation.test.tsx +47 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +6 -7
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +7 -9
- package/src/components/resources/renderers/KnativeReferenceLink.tsx +14 -0
- package/src/components/resources/renderers/KnativeReferenceLinks.test.tsx +60 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +9 -11
- package/src/components/resources/renderers/KustomizationRenderer.tsx +2 -2
- package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +3 -2
- package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +4 -3
- package/src/components/resources/renderers/LeaseRenderer.tsx +3 -15
- package/src/components/resources/renderers/ManagedResourceRenderer.tsx +2 -2
- package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +3 -2
- package/src/components/resources/renderers/NamespaceRenderer.test.ts +69 -2
- package/src/components/resources/renderers/NamespaceRenderer.tsx +6 -1
- package/src/components/resources/renderers/NodeRenderer.test.tsx +81 -1
- package/src/components/resources/renderers/NodeRenderer.tsx +56 -15
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +3 -3
- package/src/components/resources/renderers/PodClassReferences.test.tsx +42 -0
- package/src/components/resources/renderers/PodNomination.test.tsx +57 -0
- package/src/components/resources/renderers/PodRenderer.test.tsx +32 -0
- package/src/components/resources/renderers/PodRenderer.tsx +62 -36
- package/src/components/resources/renderers/RolloutRenderer.test.tsx +1 -1
- package/src/components/resources/renderers/RolloutRenderer.tsx +3 -2
- package/src/components/resources/renderers/SbomReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/SecretRenderer.test.tsx +88 -0
- package/src/components/resources/renderers/SecretRenderer.tsx +34 -5
- package/src/components/resources/renderers/ServiceRenderer.test.tsx +43 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +11 -3
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +9 -22
- package/src/components/resources/renderers/VPARenderer.tsx +17 -11
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +5 -7
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +3 -3
- package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +2 -2
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +3 -3
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +2 -2
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +2 -2
- package/src/components/resources/renderers/WorkflowRenderer.tsx +4 -3
- package/src/components/resources/renderers/kyverno-modern-cells.tsx +1 -1
- package/src/components/resources/renderers/rollout/ReplicaSetProgression.tsx +2 -2
- package/src/components/resources/resource-utils-capi.ts +1 -1
- package/src/components/resources/resource-utils-cnpg.test.ts +35 -10
- package/src/components/resources/resource-utils-cnpg.ts +51 -24
- package/src/components/resources/resource-utils-contour.test.ts +44 -0
- package/src/components/resources/resource-utils-contour.ts +16 -0
- package/src/components/resources/resource-utils-istio.ts +12 -8
- package/src/components/resources/resource-utils-keda.ts +1 -7
- package/src/components/resources/resource-utils-knative.ts +18 -0
- package/src/components/resources/resource-utils-kyverno.ts +1 -7
- package/src/components/resources/resource-utils-velero.ts +0 -12
- package/src/components/resources/resource-utils.ts +99 -39
- package/src/components/resources/rules-column-width.test.ts +1 -1
- package/src/components/resources/summarize-scheduler-message.test.ts +30 -0
- package/src/components/shared/ActionConfirmDialog.test.tsx +154 -0
- package/src/components/shared/ActionConfirmDialog.tsx +246 -0
- package/src/components/shared/CreateResourceDialog.test.tsx +330 -0
- package/src/components/shared/CreateResourceDialog.tsx +126 -80
- package/src/components/shared/DetailShell.test.tsx +80 -0
- package/src/components/shared/DetailShell.tsx +85 -10
- package/src/components/shared/DrainPlanDialog.tsx +1 -0
- package/src/components/shared/EditableYamlView.test.tsx +7 -0
- package/src/components/shared/EditableYamlView.tsx +1 -1
- package/src/components/shared/GitOpsWriteWarning.test.tsx +86 -0
- package/src/components/shared/GitOpsWriteWarning.tsx +135 -0
- package/src/components/shared/ResourceActionsBar.tsx +20 -24
- package/src/components/shared/ResourceRendererDispatch.tsx +13 -10
- package/src/components/shared/SetImageDialog.test.ts +75 -6
- package/src/components/shared/SetImageDialog.tsx +92 -84
- package/src/components/shared/index.ts +4 -1
- package/src/components/timeline/TimelineList.tsx +2 -15
- package/src/components/timeline/TimelineSwimlanes.tsx +3 -11
- package/src/components/timeline/timeline-filters.ts +1 -0
- package/src/components/topology/K8sResourceNode.test.ts +18 -1
- package/src/components/topology/K8sResourceNode.tsx +44 -2
- package/src/components/topology/TopologyFilterSidebar.tsx +1 -0
- package/src/components/topology/TopologyGraph.tsx +8 -74
- package/src/components/topology/TopologySearch.tsx +1 -0
- package/src/components/topology/internet-groups.test.ts +35 -0
- package/src/components/topology/internet-groups.ts +55 -0
- package/src/components/topology/layout.ts +1 -1
- package/src/components/ui/Badge.tsx +1 -0
- package/src/components/ui/FoldSection.tsx +68 -0
- package/src/components/ui/ForceDeleteConfirmDialog.test.tsx +62 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +24 -12
- package/src/components/ui/RefLink.tsx +20 -0
- package/src/components/ui/RowActionMenu.tsx +8 -3
- package/src/components/ui/ScheduleValue.test.tsx +5 -0
- package/src/components/ui/ScheduleValue.tsx +3 -2
- package/src/components/ui/crd-definition-navigation.test.tsx +160 -0
- package/src/components/ui/drawer-components.test.tsx +175 -1
- package/src/components/ui/drawer-components.tsx +103 -36
- package/src/components/ui/index.ts +3 -1
- package/src/components/ui/monacoEditorFeatures.ts +77 -0
- package/src/components/ui/monacoRuntime.ts +14 -19
- package/src/components/ui/status-tone.test.ts +10 -1
- package/src/components/ui/status-tone.tsx +29 -0
- package/src/components/workload/WorkloadView.deleted.test.tsx +101 -0
- package/src/components/workload/WorkloadView.deletedDialog.test.tsx +45 -0
- package/src/components/workload/WorkloadView.relationships.test.tsx +37 -0
- package/src/components/workload/WorkloadView.secretSave.test.tsx +62 -0
- package/src/components/workload/WorkloadView.spec.test.tsx +149 -0
- package/src/components/workload/WorkloadView.tsx +297 -104
- package/src/index.ts +14 -1
- package/src/monaco-deep.d.ts +19 -12
- package/src/theme/components.css +13 -0
- package/src/types/capacity.ts +21 -2
- package/src/types/core.ts +60 -2
- package/src/types/gitops-flux-status.test.ts +32 -1
- package/src/types/gitops-insights.ts +19 -2
- package/src/types/gitops-tree.ts +10 -0
- package/src/types/gitops.ts +26 -6
- package/src/utils/api-resources.ts +7 -0
- package/src/utils/application-history.ts +1 -0
- package/src/utils/cnpg-backup.test.ts +26 -0
- package/src/utils/cnpg-backup.ts +70 -0
- package/src/utils/format.test.ts +49 -2
- package/src/utils/format.ts +9 -1
- package/src/utils/gateway-references.test.ts +17 -0
- package/src/utils/gateway-references.ts +29 -0
- package/src/utils/gitops-owner.ts +3 -2
- package/src/utils/gitops-write-guard.test.ts +401 -0
- package/src/utils/gitops-write-guard.ts +509 -0
- package/src/utils/grant.test.ts +19 -0
- package/src/utils/grant.ts +35 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/ingress-class-references.ts +22 -0
- package/src/utils/k8s-errors.ts +7 -7
- package/src/utils/log-format.test.ts +41 -0
- package/src/utils/log-format.ts +71 -15
- package/src/utils/log-level.ts +12 -8
- package/src/utils/navigation.test.ts +70 -0
- package/src/utils/navigation.ts +91 -8
- package/src/utils/node-lifecycle.test.ts +36 -0
- package/src/utils/node-lifecycle.ts +77 -0
- package/src/utils/object-reference-navigation.test.ts +43 -0
- package/src/utils/resource-hierarchy.test.ts +69 -0
- package/src/utils/resource-hierarchy.ts +41 -7
- package/src/utils/resource-icons.ts +2 -0
- package/src/utils/resource-refs.test.ts +23 -0
- package/src/utils/resource-refs.ts +30 -0
- package/src/utils/skeleton-yaml.test.ts +21 -0
- package/src/utils/skeleton-yaml.ts +16 -0
- package/src/utils/url-path.test.ts +35 -0
- package/src/utils/url-path.ts +21 -0
- package/src/utils/validators.test.ts +4 -13
- package/src/utils/validators.ts +5 -19
- package/src/utils/view-transition.test.ts +35 -1
- 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('
|
|
374
|
-
expect(s.text).toBe('
|
|
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('
|
|
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: '
|
|
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: '
|
|
724
|
+
.toMatchObject({ text: '2 instances requested', level: 'neutral' })
|
|
700
725
|
expect(getCNPGPoolerStatus({ spec: { instances: 2, pgbouncer: { paused: false } }, status: { instances: 2 } }))
|
|
701
|
-
.toMatchObject({ text: '
|
|
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
|
|
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('
|
|
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('
|
|
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('
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
|
|
448
|
-
|
|
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: '
|
|
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.
|
|
970
|
+
return { text: 'Immediate run requested', color: healthColors.neutral, level: 'neutral' }
|
|
946
971
|
}
|
|
947
972
|
|
|
948
|
-
return { text: '
|
|
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: '
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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 =
|
|
116
|
+
const parsed = resolveIstioServiceHost(dest.destination.host, resource.metadata?.namespace || '', serviceDomain)
|
|
113
117
|
destinations.push({
|
|
114
118
|
host: dest.destination.host,
|
|
115
|
-
namespace: parsed
|
|
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
|
|
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
|
|
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.
|