@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
|
@@ -108,8 +108,8 @@ describe('curated column ownership', () => {
|
|
|
108
108
|
// Brace-counting, not parsing: assert the extraction actually saw the whole
|
|
109
109
|
// table, or a silently-missed key would let the guard above pass vacuously.
|
|
110
110
|
it('extracted every curated key', () => {
|
|
111
|
-
expect(CURATED.length).toBe(
|
|
112
|
-
expect(OWNED.size).toBe(
|
|
111
|
+
expect(CURATED.length).toBe(240)
|
|
112
|
+
expect(OWNED.size).toBe(202)
|
|
113
113
|
expect(CURATED).toContain('pods')
|
|
114
114
|
expect(CURATED).toContain('crossplanemanagedresources')
|
|
115
115
|
expect([...OWNED]).toContain('awsmanagedcontrolplanes')
|
|
@@ -166,7 +166,7 @@ describe('every declaration resolves', () => {
|
|
|
166
166
|
.map(m => [m[1], m[2].split(',').map(g => g.trim().replace(/'/g, ''))] as const)
|
|
167
167
|
|
|
168
168
|
it('covers the whole ownership table', () => {
|
|
169
|
-
expect(DECLARED.length).toBe(
|
|
169
|
+
expect(DECLARED.length).toBe(202)
|
|
170
170
|
})
|
|
171
171
|
|
|
172
172
|
it.each(DECLARED)('%s is curated under each group it claims', (key, groups) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Activity, BarChart3, Server } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, AlertBanner } from '../../ui/drawer-components'
|
|
3
3
|
import { Badge, type BadgeSeverity } from '../../ui/Badge'
|
|
4
|
-
import {
|
|
4
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
5
5
|
|
|
6
6
|
interface AnalysisRunRendererProps {
|
|
7
7
|
data: any
|
|
@@ -60,8 +60,8 @@ export function AnalysisRunRenderer({ data, onNavigate }: AnalysisRunRendererPro
|
|
|
60
60
|
<Property label="Phase" value={<Badge severity={phaseSeverity(phase)}>{phase}</Badge>} />
|
|
61
61
|
{labels['rollout-type'] && <Property label="Triggered as" value={labels['rollout-type']} />}
|
|
62
62
|
{labels['step-index'] && <Property label="Canary step" value={labels['step-index']} />}
|
|
63
|
-
<Property label="Started" value={status.startedAt ?
|
|
64
|
-
<Property label="Completed" value={status.completedAt ?
|
|
63
|
+
<Property label="Started" value={status.startedAt ? <TimeValue timestamp={status.startedAt} /> : undefined} />
|
|
64
|
+
<Property label="Completed" value={status.completedAt ? <TimeValue timestamp={status.completedAt} /> : undefined} />
|
|
65
65
|
<Property label="Message" value={status.message} />
|
|
66
66
|
{ownerRollout && (
|
|
67
67
|
<Property
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { GitBranch, FolderTree, Settings, Target, XCircle, History, ListChecks, ExternalLink } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts } from '../../ui/drawer-components'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { GitOpsStatusBadge, ManagedResourcesList, SyncCountdown } from '../../gitops'
|
|
6
5
|
import {
|
|
7
6
|
argoStatusToGitOpsStatus,
|
|
@@ -11,6 +10,8 @@ import {
|
|
|
11
10
|
} from '../../../types/gitops'
|
|
12
11
|
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
13
12
|
import { buildRepoBrowseUrl, buildPathBrowseUrl } from '../../../utils/git-provider-urls'
|
|
13
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
14
|
+
import { formatRelativeAgeTime } from '../../../utils/format'
|
|
14
15
|
|
|
15
16
|
const REPO_LINK_CLASS = 'text-blue-400 hover:text-blue-300 hover:underline break-all'
|
|
16
17
|
|
|
@@ -297,10 +298,10 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
|
|
|
297
298
|
<Property label="Message" value={operationState.message} />
|
|
298
299
|
)}
|
|
299
300
|
{operationState.startedAt && (
|
|
300
|
-
<Property label="Started" value={
|
|
301
|
+
<Property label="Started" value={<TimeValue timestamp={operationState.startedAt} />} />
|
|
301
302
|
)}
|
|
302
303
|
{operationState.finishedAt && (
|
|
303
|
-
<Property label="Finished" value={
|
|
304
|
+
<Property label="Finished" value={<TimeValue timestamp={operationState.finishedAt} />} />
|
|
304
305
|
)}
|
|
305
306
|
{operationState.retryCount != null && (
|
|
306
307
|
<Property
|
|
@@ -402,7 +403,7 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
|
|
|
402
403
|
</span>
|
|
403
404
|
{entry.deployedAt && (
|
|
404
405
|
<span className="text-xs text-theme-text-tertiary">
|
|
405
|
-
{
|
|
406
|
+
{formatRelativeAgeTime(entry.deployedAt)}
|
|
406
407
|
</span>
|
|
407
408
|
)}
|
|
408
409
|
</div>
|
|
@@ -425,7 +426,7 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
|
|
|
425
426
|
<Property label="Current Revision" value={status.sync.revision} />
|
|
426
427
|
)}
|
|
427
428
|
{status.reconciledAt && (
|
|
428
|
-
<Property label="Last Reconciled" value={
|
|
429
|
+
<Property label="Last Reconciled" value={<TimeValue timestamp={status.reconciledAt} />} />
|
|
429
430
|
)}
|
|
430
431
|
</PropertyList>
|
|
431
432
|
</Section>
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act } from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
5
|
+
import { expect, it, vi } from "vitest";
|
|
6
|
+
import { HPARenderer } from "./HPARenderer";
|
|
7
|
+
import { VPARenderer } from "./VPARenderer";
|
|
8
|
+
import { KedaScaledObjectRenderer } from "./KedaScaledObjectRenderer";
|
|
9
|
+
import {
|
|
10
|
+
initNavigationMap,
|
|
11
|
+
resetNavigationMap,
|
|
12
|
+
} from "../../../utils/navigation";
|
|
13
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });
|
|
14
|
+
|
|
15
|
+
it.each(["HPA", "VPA"])(
|
|
16
|
+
"retains a %s custom scale target API group",
|
|
17
|
+
async (kind) => {
|
|
18
|
+
initNavigationMap([
|
|
19
|
+
{
|
|
20
|
+
group: "custom.example.io",
|
|
21
|
+
version: "v1",
|
|
22
|
+
kind: "Deployment",
|
|
23
|
+
name: "deployments",
|
|
24
|
+
namespaced: true,
|
|
25
|
+
isCrd: true,
|
|
26
|
+
verbs: ["get"],
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
const element = document.createElement("div");
|
|
30
|
+
const root = createRoot(element);
|
|
31
|
+
const onNavigate = vi.fn();
|
|
32
|
+
const target = {
|
|
33
|
+
apiVersion: "custom.example.io/v1",
|
|
34
|
+
kind: "Deployment",
|
|
35
|
+
name: "same-name",
|
|
36
|
+
};
|
|
37
|
+
const data = {
|
|
38
|
+
metadata: { namespace: "team" },
|
|
39
|
+
spec: kind === "HPA" ? { scaleTargetRef: target } : { targetRef: target },
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
await act(async () =>
|
|
43
|
+
root.render(
|
|
44
|
+
kind === "HPA" ? (
|
|
45
|
+
<HPARenderer data={data} onNavigate={onNavigate} />
|
|
46
|
+
) : (
|
|
47
|
+
<VPARenderer data={data} onNavigate={onNavigate} />
|
|
48
|
+
),
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
const button = [...element.querySelectorAll("button")].find(
|
|
52
|
+
(b) => b.textContent === "Deployment/same-name",
|
|
53
|
+
);
|
|
54
|
+
expect(button).toBeDefined();
|
|
55
|
+
expect([...element.querySelectorAll("button")].filter(b => b.textContent?.startsWith("Deployment/same-name"))).toHaveLength(1);
|
|
56
|
+
await act(async () => button!.click());
|
|
57
|
+
expect(onNavigate).toHaveBeenCalledWith({
|
|
58
|
+
kind: "Deployment",
|
|
59
|
+
namespace: "team",
|
|
60
|
+
name: "same-name",
|
|
61
|
+
group: "custom.example.io",
|
|
62
|
+
});
|
|
63
|
+
} finally {
|
|
64
|
+
await act(async () => root.unmount());
|
|
65
|
+
resetNavigationMap();
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
it.each(["v1", "", undefined])("keeps Object metric resources separate from the scale target and measures its own Namespace (apiVersion %s)", async (namespaceAPIVersion) => {
|
|
71
|
+
const element = document.createElement("div");
|
|
72
|
+
const root = createRoot(element);
|
|
73
|
+
const onNavigate = vi.fn();
|
|
74
|
+
const data = {
|
|
75
|
+
metadata: { namespace: "team" },
|
|
76
|
+
spec: {
|
|
77
|
+
scaleTargetRef: {
|
|
78
|
+
apiVersion: "apps/v1",
|
|
79
|
+
kind: "Deployment",
|
|
80
|
+
name: "workload",
|
|
81
|
+
},
|
|
82
|
+
metrics: [
|
|
83
|
+
{
|
|
84
|
+
type: "Object",
|
|
85
|
+
object: {
|
|
86
|
+
metric: { name: "requests" },
|
|
87
|
+
describedObject: {
|
|
88
|
+
apiVersion: "networking.k8s.io/v1",
|
|
89
|
+
kind: "Ingress",
|
|
90
|
+
name: "frontend",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: "Object",
|
|
96
|
+
object: {
|
|
97
|
+
metric: { name: "queue" },
|
|
98
|
+
describedObject: {
|
|
99
|
+
apiVersion: "custom.example.io/v1",
|
|
100
|
+
kind: "Widget",
|
|
101
|
+
name: "input",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: "Object",
|
|
107
|
+
object: {
|
|
108
|
+
metric: { name: "namespace-metric" },
|
|
109
|
+
describedObject: {
|
|
110
|
+
apiVersion: namespaceAPIVersion,
|
|
111
|
+
kind: "Namespace",
|
|
112
|
+
name: "cannot-escape",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
try {
|
|
120
|
+
await act(async () =>
|
|
121
|
+
root.render(<HPARenderer data={data} onNavigate={onNavigate} />),
|
|
122
|
+
);
|
|
123
|
+
for (const [label, expected] of [
|
|
124
|
+
[
|
|
125
|
+
"Ingress/frontend",
|
|
126
|
+
{ kind: "Ingress", group: "networking.k8s.io", namespace: "team", name: "frontend" },
|
|
127
|
+
],
|
|
128
|
+
[
|
|
129
|
+
"Widget/input",
|
|
130
|
+
{
|
|
131
|
+
kind: "Widget",
|
|
132
|
+
group: "custom.example.io",
|
|
133
|
+
namespace: "team",
|
|
134
|
+
name: "input",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
"Namespace/team",
|
|
139
|
+
{ kind: "Namespace", group: "", namespace: "", name: "team" },
|
|
140
|
+
],
|
|
141
|
+
] as const) {
|
|
142
|
+
const button = [...element.querySelectorAll("button")].find(
|
|
143
|
+
(b) => b.textContent === label,
|
|
144
|
+
);
|
|
145
|
+
expect(button).toBeDefined();
|
|
146
|
+
await act(async () => button!.click());
|
|
147
|
+
expect(onNavigate).toHaveBeenLastCalledWith(expected);
|
|
148
|
+
}
|
|
149
|
+
expect(element.textContent).toContain("Object metric sources");
|
|
150
|
+
expect(element.textContent).toContain("This HPA's namespace");
|
|
151
|
+
expect(element.textContent).not.toContain("Namespace/cannot-escape");
|
|
152
|
+
} finally {
|
|
153
|
+
await act(async () => root.unmount());
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it.each(["HPA", "VPA"])("does not infer missing %s target identity", (kind) => {
|
|
158
|
+
const target = { name: "ambiguous" };
|
|
159
|
+
const data = {
|
|
160
|
+
metadata: { namespace: "team" },
|
|
161
|
+
spec: kind === "HPA" ? { scaleTargetRef: target } : { targetRef: target },
|
|
162
|
+
};
|
|
163
|
+
const html = renderToStaticMarkup(
|
|
164
|
+
kind === "HPA" ? (
|
|
165
|
+
<HPARenderer data={data} onNavigate={() => {}} />
|
|
166
|
+
) : (
|
|
167
|
+
<VPARenderer data={data} onNavigate={() => {}} />
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
171
|
+
expect(
|
|
172
|
+
[...doc.querySelectorAll("button")].some((b) =>
|
|
173
|
+
b.textContent?.includes("ambiguous"),
|
|
174
|
+
),
|
|
175
|
+
).toBe(false);
|
|
176
|
+
expect(html).not.toContain("Deployment/ambiguous");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it.each([
|
|
180
|
+
[
|
|
181
|
+
{ name: "worker" },
|
|
182
|
+
{ kind: "Deployment", group: "apps", namespace: "team", name: "worker" },
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
{ name: "worker", apiVersion: "", kind: "" },
|
|
186
|
+
{ kind: "Deployment", group: "apps", namespace: "team", name: "worker" },
|
|
187
|
+
],
|
|
188
|
+
[
|
|
189
|
+
{ apiVersion: "custom.example.io/v1", kind: "Deployment", name: "worker" },
|
|
190
|
+
{
|
|
191
|
+
kind: "Deployment",
|
|
192
|
+
group: "custom.example.io",
|
|
193
|
+
namespace: "team",
|
|
194
|
+
name: "worker",
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
])(
|
|
198
|
+
"preserves KEDA documented defaults and explicit custom API identity",
|
|
199
|
+
async (target, expected) => {
|
|
200
|
+
const element = document.createElement("div");
|
|
201
|
+
const root = createRoot(element);
|
|
202
|
+
const onNavigate = vi.fn();
|
|
203
|
+
try {
|
|
204
|
+
await act(async () =>
|
|
205
|
+
root.render(
|
|
206
|
+
<KedaScaledObjectRenderer
|
|
207
|
+
data={{
|
|
208
|
+
metadata: { namespace: "team" },
|
|
209
|
+
spec: { scaleTargetRef: target },
|
|
210
|
+
}}
|
|
211
|
+
onNavigate={onNavigate}
|
|
212
|
+
/>,
|
|
213
|
+
),
|
|
214
|
+
);
|
|
215
|
+
const button = [...element.querySelectorAll("button")].find(
|
|
216
|
+
(b) => b.textContent === "Deployment/worker",
|
|
217
|
+
);
|
|
218
|
+
expect(button).toBeDefined();
|
|
219
|
+
await act(async () => button!.click());
|
|
220
|
+
expect(onNavigate).toHaveBeenCalledWith(expected);
|
|
221
|
+
} finally {
|
|
222
|
+
await act(async () => root.unmount());
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
it.each([
|
|
228
|
+
["Deployment", "apps"], ["StatefulSet", "apps"], ["DaemonSet", "apps"], ["ReplicaSet", "apps"],
|
|
229
|
+
["Job", "batch"], ["CronJob", "batch"], ["ReplicationController", ""],
|
|
230
|
+
])("keeps versionless VPA %s target navigation", async (kind, group) => {
|
|
231
|
+
const element = document.createElement("div");
|
|
232
|
+
const root = createRoot(element);
|
|
233
|
+
const onNavigate = vi.fn();
|
|
234
|
+
try {
|
|
235
|
+
await act(async () => root.render(<VPARenderer data={{ metadata: { namespace: "team" }, spec: { targetRef: { kind, name: "workload" } } }} onNavigate={onNavigate} />));
|
|
236
|
+
const link = [...element.querySelectorAll("button")].find(button => button.textContent === `${kind}/workload`);
|
|
237
|
+
expect(link).toBeDefined();
|
|
238
|
+
await act(async () => link!.click());
|
|
239
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ kind, group, namespace: "team", name: "workload" });
|
|
240
|
+
} finally {
|
|
241
|
+
await act(async () => root.unmount());
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("pairs each diagnosis metric card with its declared Object source once", async () => {
|
|
246
|
+
const element = document.createElement("div");
|
|
247
|
+
const root = createRoot(element);
|
|
248
|
+
const onNavigate = vi.fn();
|
|
249
|
+
const objects = [
|
|
250
|
+
{ metric: { name: "requests" }, describedObject: { apiVersion: "v1", kind: "Endpoints", name: "input" } },
|
|
251
|
+
{ metric: { name: "requests" }, describedObject: { apiVersion: "custom.example.io/v1", kind: "Endpoints", name: "input" } },
|
|
252
|
+
{ metric: { name: "queue_depth" }, describedObject: { kind: "Namespace", name: "ignored" } },
|
|
253
|
+
];
|
|
254
|
+
try {
|
|
255
|
+
await act(async () => root.render(<HPARenderer
|
|
256
|
+
data={{ metadata: { namespace: "team" }, spec: { metrics: objects.map(object => ({ type: "Object", object })) } }}
|
|
257
|
+
hpaDiagnosis={{ state: "stabilized", summary: "Stabilized", target: {}, bounds: { min: 1, max: 3, current: 2, desired: 2 }, metrics: [
|
|
258
|
+
{ type: "Object", name: "Endpoints/input/requests", current: "10", status: "ok" },
|
|
259
|
+
{ type: "Object", name: "Endpoints (custom.example.io)/input/requests", current: "20", status: "ok" },
|
|
260
|
+
{ type: "Object", name: "Namespace/team/queue_depth", current: "7", status: "ok" },
|
|
261
|
+
{ type: "Object", name: "Service/stale/requests", current: "3", status: "status_only" },
|
|
262
|
+
] }} onNavigate={onNavigate} />));
|
|
263
|
+
expect(element.textContent).not.toContain("Object metric sources");
|
|
264
|
+
expect(element.textContent).not.toContain("ignored");
|
|
265
|
+
const endpoints = [...element.querySelectorAll("button")].filter(button => button.textContent === "Endpoints/input");
|
|
266
|
+
expect(endpoints).toHaveLength(2);
|
|
267
|
+
await act(async () => endpoints[0].click());
|
|
268
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ kind: "Endpoints", group: "", namespace: "team", name: "input" });
|
|
269
|
+
await act(async () => endpoints[1].click());
|
|
270
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ kind: "Endpoints", group: "custom.example.io", namespace: "team", name: "input" });
|
|
271
|
+
const namespace = [...element.querySelectorAll("button")].find(button => button.textContent === "Namespace/team");
|
|
272
|
+
expect(namespace).toBeDefined();
|
|
273
|
+
await act(async () => namespace!.click());
|
|
274
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ kind: "Namespace", group: "", namespace: "", name: "team" });
|
|
275
|
+
expect([...element.querySelectorAll("button")].some(button => button.textContent === "Service/stale")).toBe(false);
|
|
276
|
+
for (const value of ["Current 10", "Current 20", "Current 7"]) expect(element.textContent).toContain(value);
|
|
277
|
+
} finally {
|
|
278
|
+
await act(async () => root.unmount());
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("shows the documented default Target API for KEDA", () => {
|
|
283
|
+
const html = renderToStaticMarkup(<KedaScaledObjectRenderer data={{ metadata: { namespace: "team" }, spec: { scaleTargetRef: { name: "worker" } } }} />);
|
|
284
|
+
expect(html).toContain("Target API");
|
|
285
|
+
expect(html).toContain("apps/v1");
|
|
286
|
+
});
|
|
@@ -2,8 +2,8 @@ import { useState } from 'react'
|
|
|
2
2
|
import { Server, Globe, Network, Layers, Download, CheckCircle, AlertCircle } from 'lucide-react'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
4
4
|
import { kindToPlural } from '../../../utils/navigation'
|
|
5
|
-
import { formatAge } from '../resource-utils'
|
|
6
5
|
import { getClusterStatus, getClusterClass, getClusterVersion, getClusterEndpoint, getProviderFromInfraKind, parseCAPIConditionMessage } from '../resource-utils-capi'
|
|
6
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
data: any
|
|
@@ -27,6 +27,7 @@ export function CAPIClusterRenderer({ data, onNavigate, apiBase = '', canConnect
|
|
|
27
27
|
const className = getClusterClass(data)
|
|
28
28
|
const version = getClusterVersion(data)
|
|
29
29
|
const topology = spec.topology || {}
|
|
30
|
+
const classNamespace = (topology.classRef?.name ? topology.classRef.namespace : topology.classNamespace) || data.metadata?.namespace || ''
|
|
30
31
|
|
|
31
32
|
// v1beta2 replica fields
|
|
32
33
|
const cpReady = status.controlPlane?.readyReplicas ?? status.controlPlane?.replicas
|
|
@@ -119,7 +120,7 @@ export function CAPIClusterRenderer({ data, onNavigate, apiBase = '', canConnect
|
|
|
119
120
|
{className !== '-' && <Property label="Cluster Class" value={className} />}
|
|
120
121
|
{endpoint !== '-' && <Property label="Control Plane Endpoint" value={endpoint} />}
|
|
121
122
|
{readyCond?.lastTransitionTime && (
|
|
122
|
-
<Property label="Since" value={
|
|
123
|
+
<Property label="Since" value={<TimeValue timestamp={readyCond.lastTransitionTime} />} />
|
|
123
124
|
)}
|
|
124
125
|
</PropertyList>
|
|
125
126
|
</Section>
|
|
@@ -201,10 +202,10 @@ export function CAPIClusterRenderer({ data, onNavigate, apiBase = '', canConnect
|
|
|
201
202
|
</Section>
|
|
202
203
|
|
|
203
204
|
{/* Topology (ClusterClass-based) */}
|
|
204
|
-
{
|
|
205
|
+
{className !== '-' && (
|
|
205
206
|
<Section title="Topology" icon={Layers}>
|
|
206
207
|
<PropertyList>
|
|
207
|
-
<Property label="Class" value={
|
|
208
|
+
<Property label="Class" value={<ResourceLink name={className} kind="clusterclasses" group="cluster.x-k8s.io" namespace={classNamespace} onNavigate={onNavigate} />} />
|
|
208
209
|
{topology.version && <Property label="Version" value={topology.version} />}
|
|
209
210
|
{topology.controlPlane?.replicas != null && (
|
|
210
211
|
<Property label="CP Replicas" value={String(topology.controlPlane.replicas)} />
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act, type ReactNode } from 'react'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
5
|
+
import { CAPIClusterRenderer } from './CAPIClusterRenderer'
|
|
6
|
+
|
|
7
|
+
async function rendered(element: ReactNode, check: (container: HTMLDivElement) => Promise<void> | void) {
|
|
8
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
|
9
|
+
const container = document.createElement('div')
|
|
10
|
+
const root = createRoot(container)
|
|
11
|
+
try { await act(async () => root.render(element)); await check(container) }
|
|
12
|
+
finally { await act(async () => root.unmount()); Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: false }) }
|
|
13
|
+
}
|
|
14
|
+
async function click(container: HTMLDivElement, text: string) {
|
|
15
|
+
const button = [...container.querySelectorAll('button')].find(button => button.textContent === text)
|
|
16
|
+
expect(button, `missing link ${text}`).toBeDefined()
|
|
17
|
+
await act(async () => button!.click())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('exact reference navigation', () => {
|
|
21
|
+
it('uses the v1beta2 ClusterClass reference namespace',async()=>{
|
|
22
|
+
const navigate=vi.fn()
|
|
23
|
+
await rendered(<CAPIClusterRenderer data={{metadata:{namespace:'app'},spec:{topology:{classRef:{name:'standard',namespace:'classes'}}}}} onNavigate={navigate}/>,async container=>{
|
|
24
|
+
await click(container,'standard');expect(navigate).toHaveBeenCalledWith({kind:'clusterclasses',group:'cluster.x-k8s.io',namespace:'classes',name:'standard'})
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Server, Shield, Settings } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
3
3
|
import { kindToPlural } from '../../../utils/navigation'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { getKCPStatus, getKCPVersion, getKCPInitialized, getMachineClusterName } from '../resource-utils-capi'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
8
|
data: any
|
|
@@ -50,7 +50,7 @@ export function CAPIKubeadmControlPlaneRenderer({ data, onNavigate }: Props) {
|
|
|
50
50
|
<Property label="Initialized" value={initialized ? 'Yes' : 'No'} />
|
|
51
51
|
{updateStrategy && <Property label="Update Strategy" value={updateStrategy} />}
|
|
52
52
|
{readyCond?.lastTransitionTime && (
|
|
53
|
-
<Property label="Since" value={
|
|
53
|
+
<Property label="Since" value={<TimeValue timestamp={readyCond.lastTransitionTime} />} />
|
|
54
54
|
)}
|
|
55
55
|
</PropertyList>
|
|
56
56
|
</Section>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Server, Settings } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
3
3
|
import { kindToPlural } from '../../../utils/navigation'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { getMachineDeploymentStatus, getMachineDeploymentVersion, getMachineClusterName } from '../resource-utils-capi'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
8
|
data: any
|
|
@@ -56,7 +56,7 @@ export function CAPIMachineDeploymentRenderer({ data, onNavigate }: Props) {
|
|
|
56
56
|
<Property label="Cluster" value={clusterName} />
|
|
57
57
|
<Property label="Version" value={version} />
|
|
58
58
|
{readyCond?.lastTransitionTime && (
|
|
59
|
-
<Property label="Since" value={
|
|
59
|
+
<Property label="Since" value={<TimeValue timestamp={readyCond.lastTransitionTime} />} />
|
|
60
60
|
)}
|
|
61
61
|
</PropertyList>
|
|
62
62
|
</Section>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Server, Settings } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
3
3
|
import { kindToPlural } from '../../../utils/navigation'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { getMachinePoolStatus } from '../resource-utils-capi'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
8
|
data: any
|
|
@@ -44,7 +44,7 @@ export function CAPIMachinePoolRenderer({ data, onNavigate }: Props) {
|
|
|
44
44
|
<Property label="Min Ready Seconds" value={String(spec.minReadySeconds)} />
|
|
45
45
|
)}
|
|
46
46
|
{readyCond?.lastTransitionTime && (
|
|
47
|
-
<Property label="Since" value={
|
|
47
|
+
<Property label="Since" value={<TimeValue timestamp={readyCond.lastTransitionTime} />} />
|
|
48
48
|
)}
|
|
49
49
|
</PropertyList>
|
|
50
50
|
</Section>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Cpu, Server, Network, Cloud } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
3
3
|
import { kindToPlural } from '../../../utils/navigation'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { getMachineStatus, getMachineRole, getMachineClusterName, getMachineNodeRef, getMachineVersion, getMachineProviderID, parseProviderID, getProviderFromInfraKind, parseCAPIConditionMessage } from '../resource-utils-capi'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
8
|
data: any
|
|
@@ -59,7 +59,7 @@ export function CAPIMachineRenderer({ data, onNavigate }: Props) {
|
|
|
59
59
|
{infraProvider && <Property label="Provider" value={infraProvider} />}
|
|
60
60
|
{spec.failureDomain && <Property label="Failure Domain" value={spec.failureDomain} />}
|
|
61
61
|
{readyCond?.lastTransitionTime && (
|
|
62
|
-
<Property label="Since" value={
|
|
62
|
+
<Property label="Since" value={<TimeValue timestamp={readyCond.lastTransitionTime} />} />
|
|
63
63
|
)}
|
|
64
64
|
</PropertyList>
|
|
65
65
|
</Section>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Server, Settings } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
3
3
|
import { kindToPlural } from '../../../utils/navigation'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { getMachineSetStatus, getMachineClusterName } from '../resource-utils-capi'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
8
|
data: any
|
|
@@ -46,7 +46,7 @@ export function CAPIMachineSetRenderer({ data, onNavigate }: Props) {
|
|
|
46
46
|
<Property label="Min Ready Seconds" value={String(spec.minReadySeconds)} />
|
|
47
47
|
)}
|
|
48
48
|
{readyCond?.lastTransitionTime && (
|
|
49
|
-
<Property label="Since" value={
|
|
49
|
+
<Property label="Since" value={<TimeValue timestamp={readyCond.lastTransitionTime} />} />
|
|
50
50
|
)}
|
|
51
51
|
</PropertyList>
|
|
52
52
|
</Section>
|
|
@@ -27,3 +27,26 @@ describe('CNPGBackupRenderer — spec.target is instance selection, not a restor
|
|
|
27
27
|
expect(html(backup())).not.toContain('Backup Target')
|
|
28
28
|
})
|
|
29
29
|
})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
describe('CNPGBackupRenderer plugin destination', () => {
|
|
33
|
+
it.each([undefined, { barmanObjectName: 'ignored-store', serverName: 'ignored-server' }, { serverName: 'ignored-server' }])('explains the Cluster destination and ignored parameters: %j', (parameters) => {
|
|
34
|
+
const out = renderToString(<CNPGBackupRenderer data={backup({ spec: { method: 'plugin', pluginConfiguration: { name: 'barman-cloud.cloudnative-pg.io', parameters } } })} onNavigate={() => {}} />)
|
|
35
|
+
expect(out).toContain('From the Cluster')
|
|
36
|
+
expect(out).toContain('barman-cloud plugin')
|
|
37
|
+
expect(out).not.toContain('Object Store')
|
|
38
|
+
expect(out).not.toContain('ignored-store')
|
|
39
|
+
expect(out).not.toContain('ignored-server')
|
|
40
|
+
expect(out.match(/<button[^>]*>(?:pg|ignored-store|ignored-server)<\/button>/g)).toHaveLength(1)
|
|
41
|
+
expect(out.includes('Ignored by the barman-cloud plugin')).toBe(!!parameters)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('does not assert a third-party parameter is an ObjectStore destination', () => {
|
|
45
|
+
const out = html(backup({ spec: { method: 'plugin', pluginConfiguration: { name: 'other.example.com', parameters: { barmanObjectName: 'custom-store' } } } }))
|
|
46
|
+
expect(out).toContain('other.example.com')
|
|
47
|
+
expect(out).toContain('Unknown: Radar does not model this plugin')
|
|
48
|
+
expect(out).not.toContain('custom-store')
|
|
49
|
+
expect(out).not.toContain('Object Store')
|
|
50
|
+
expect(out).not.toContain('Ignored by')
|
|
51
|
+
})
|
|
52
|
+
})
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getCNPGBackupServerName,
|
|
13
13
|
getCNPGBackupError,
|
|
14
14
|
getCNPGBackupTarget,
|
|
15
|
-
|
|
15
|
+
CNPG_BARMAN_PLUGIN_NAME,
|
|
16
16
|
} from '../resource-utils-cnpg'
|
|
17
17
|
|
|
18
18
|
interface CNPGBackupRendererProps {
|
|
@@ -51,24 +51,16 @@ export function CNPGBackupRenderer({ data, onNavigate }: CNPGBackupRendererProps
|
|
|
51
51
|
<PropertyList>
|
|
52
52
|
<Property label="Phase" value={phase} />
|
|
53
53
|
<Property label="Method" value={getCNPGBackupMethod(data)} />
|
|
54
|
-
{/* A plugin-taken backup lands in an ObjectStore rather than in the
|
|
55
|
-
Cluster's own barmanObjectStore, so naming the plugin is what
|
|
56
|
-
tells an operator where to go looking. */}
|
|
57
54
|
{backupPlugin && <Property label="Plugin" value={backupPlugin.name} />}
|
|
58
|
-
{backupPlugin
|
|
55
|
+
{backupPlugin && (
|
|
59
56
|
<Property
|
|
60
|
-
label="
|
|
61
|
-
value={
|
|
62
|
-
<ResourceLink
|
|
63
|
-
name={backupPlugin.parameters.barmanObjectName}
|
|
64
|
-
kind="objectstores"
|
|
65
|
-
group={CNPG_BARMAN_OBJECTSTORE_GROUP}
|
|
66
|
-
namespace={data.metadata?.namespace || ''}
|
|
67
|
-
onNavigate={onNavigate}
|
|
68
|
-
/>
|
|
69
|
-
}
|
|
57
|
+
label="Destination"
|
|
58
|
+
value={backupPlugin.name === CNPG_BARMAN_PLUGIN_NAME ? "From the Cluster's barman-cloud plugin" : 'Unknown: Radar does not model this plugin’s destination'}
|
|
70
59
|
/>
|
|
71
60
|
)}
|
|
61
|
+
{backupPlugin?.name === CNPG_BARMAN_PLUGIN_NAME && backupPlugin.parameters && Object.keys(backupPlugin.parameters).length > 0 && (
|
|
62
|
+
<Property label="Plugin parameters" value="Ignored by the barman-cloud plugin; the destination comes from the Cluster" />
|
|
63
|
+
)}
|
|
72
64
|
<Property label="Duration" value={getCNPGBackupDuration(data)} />
|
|
73
65
|
<Property label="Backup Name" value={getCNPGBackupName(data)} />
|
|
74
66
|
{data.status?.instanceID?.podName && (
|
|
@@ -102,10 +94,7 @@ export function CNPGBackupRenderer({ data, onNavigate }: CNPGBackupRendererProps
|
|
|
102
94
|
}
|
|
103
95
|
return clusterName
|
|
104
96
|
})()} />
|
|
105
|
-
{/*
|
|
106
|
-
Under the plugin method they are never populated because both live
|
|
107
|
-
on the ObjectStore, and rendering them as "-" reads as "not
|
|
108
|
-
configured" rather than "recorded elsewhere". */}
|
|
97
|
+
{/* Plugin backups do not report the in-tree destination fields. */}
|
|
109
98
|
{!backupPlugin && (
|
|
110
99
|
<>
|
|
111
100
|
<Property label="Destination" value={getCNPGBackupDestinationPath(data)} />
|