@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
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { KnativeReferenceLink } from './KnativeReferenceLink'
|
|
1
2
|
import { Network, ShieldCheck, Server, Globe } from 'lucide-react'
|
|
2
3
|
import { clsx } from 'clsx'
|
|
3
|
-
import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner
|
|
4
|
+
import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner } from '../../ui/drawer-components'
|
|
4
5
|
import { Badge } from '../../ui/Badge'
|
|
5
|
-
import { kindToPlural } from '../../../utils/navigation'
|
|
6
6
|
import { getKnativeConditionStatus } from '../resource-utils-knative'
|
|
7
7
|
|
|
8
8
|
interface RendererProps {
|
|
9
9
|
data: any
|
|
10
|
-
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
10
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string; group?: string }) => void
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// ============================================================================
|
|
@@ -166,9 +166,8 @@ export function ServerlessServiceRenderer({ data, onNavigate }: RendererProps) {
|
|
|
166
166
|
<Property label="Protocol" value={protocolType} />
|
|
167
167
|
{objectRef && (
|
|
168
168
|
<Property label="Target" value={
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
kind={kindToPlural(objectRef.kind || 'Deployment')}
|
|
169
|
+
<KnativeReferenceLink
|
|
170
|
+
reference={objectRef}
|
|
172
171
|
namespace={ns}
|
|
173
172
|
onNavigate={onNavigate}
|
|
174
173
|
/>
|
|
@@ -213,9 +212,8 @@ export function DomainMappingRenderer({ data, onNavigate }: RendererProps) {
|
|
|
213
212
|
) : undefined} />
|
|
214
213
|
{ref && (
|
|
215
214
|
<Property label="Target" value={
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
kind={kindToPlural(ref.kind || 'Service')}
|
|
215
|
+
<KnativeReferenceLink
|
|
216
|
+
reference={ref}
|
|
219
217
|
namespace={ns}
|
|
220
218
|
onNavigate={onNavigate}
|
|
221
219
|
/>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ResourceLink } from '../../ui/drawer-components'
|
|
2
|
+
import type { ResourceRef } from '../../../types/core'
|
|
3
|
+
import { getKnativeReference } from '../resource-utils-knative'
|
|
4
|
+
|
|
5
|
+
export function KnativeReferenceLink({ reference, namespace, onNavigate }: {
|
|
6
|
+
reference: any
|
|
7
|
+
namespace: string
|
|
8
|
+
onNavigate?: (ref: ResourceRef) => void
|
|
9
|
+
}) {
|
|
10
|
+
const resolved = getKnativeReference(reference, namespace)
|
|
11
|
+
return resolved
|
|
12
|
+
? <ResourceLink {...resolved} onNavigate={onNavigate} />
|
|
13
|
+
: <span className="text-theme-text-primary">{reference?.name || '-'}</span>
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act } from 'react'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
5
|
+
import { PingSourceRenderer } from './KnativeSourceRenderer'
|
|
6
|
+
import { DomainMappingRenderer } from './KnativeNetworkingRenderer'
|
|
7
|
+
import { initNavigationMap, resetNavigationMap } from '../../../utils/navigation'
|
|
8
|
+
|
|
9
|
+
describe('Knative reference links', () => {
|
|
10
|
+
it('keep the sink reference API group instead of resolving to the core Kind', async () => {
|
|
11
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
|
12
|
+
initNavigationMap([
|
|
13
|
+
{ group: '', version: 'v1', kind: 'Service', name: 'services', namespaced: true, isCrd: false, verbs: ['get'] },
|
|
14
|
+
{ group: 'serving.knative.dev', version: 'v1', kind: 'Service', name: 'services', namespaced: true, isCrd: true, verbs: ['get'] },
|
|
15
|
+
{ group: 'relationships.radar.test', version: 'v1', kind: 'Service', name: 'virtualservices', namespaced: true, isCrd: true, verbs: ['get'] },
|
|
16
|
+
])
|
|
17
|
+
const container = document.createElement('div')
|
|
18
|
+
const root = createRoot(container)
|
|
19
|
+
const onNavigate = vi.fn()
|
|
20
|
+
const clickSink = async (sinkRef: Record<string, string>) => {
|
|
21
|
+
await act(async () => root.render(<PingSourceRenderer onNavigate={onNavigate} data={{
|
|
22
|
+
metadata: { namespace: 'team', name: 'ping' },
|
|
23
|
+
spec: { schedule: '* * * * *', sink: { ref: sinkRef } },
|
|
24
|
+
}} />))
|
|
25
|
+
const link = [...container.querySelectorAll('button')].find(button => button.textContent === sinkRef.name)
|
|
26
|
+
expect(link).toBeDefined()
|
|
27
|
+
await act(async () => link!.click())
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
await clickSink({ apiVersion: 'serving.knative.dev/v1', kind: 'Service', name: 'knative-backend' })
|
|
31
|
+
expect(onNavigate).toHaveBeenLastCalledWith(expect.objectContaining({ kind: 'services', group: 'serving.knative.dev', namespace: 'team', name: 'knative-backend' }))
|
|
32
|
+
await clickSink({ group: 'serving.knative.dev', kind: 'Service', name: 'group-only-backend' })
|
|
33
|
+
expect(onNavigate).toHaveBeenLastCalledWith(expect.objectContaining({ kind: 'services', group: 'serving.knative.dev', name: 'group-only-backend' }))
|
|
34
|
+
await clickSink({ apiVersion: 'relationships.radar.test/v1', kind: 'Service', name: 'custom-backend' })
|
|
35
|
+
expect(onNavigate).toHaveBeenLastCalledWith(expect.objectContaining({ kind: 'virtualservices', group: 'relationships.radar.test', name: 'custom-backend' }))
|
|
36
|
+
await clickSink({ apiVersion: 'v1', kind: 'Service', name: 'core-backend' })
|
|
37
|
+
expect(onNavigate).toHaveBeenLastCalledWith(expect.objectContaining({ kind: 'services', name: 'core-backend' }))
|
|
38
|
+
expect(onNavigate.mock.lastCall?.[0].group ?? '').toBe('')
|
|
39
|
+
|
|
40
|
+
await act(async () => root.render(<PingSourceRenderer onNavigate={onNavigate} data={{
|
|
41
|
+
metadata: { namespace: 'team' }, spec: { sink: { ref: { kind: 'Service', name: 'unknown-group' } } },
|
|
42
|
+
}} />))
|
|
43
|
+
expect(container.textContent).toContain('unknown-group')
|
|
44
|
+
expect([...container.querySelectorAll('button')].some(button => button.textContent === 'unknown-group')).toBe(false)
|
|
45
|
+
|
|
46
|
+
await act(async () => root.render(<DomainMappingRenderer onNavigate={onNavigate} data={{
|
|
47
|
+
metadata: { namespace: 'team', name: 'example.com' },
|
|
48
|
+
spec: { ref: { apiVersion: 'serving.knative.dev/v1', kind: 'Service', name: 'mapped-backend' } },
|
|
49
|
+
}} />))
|
|
50
|
+
const target = [...container.querySelectorAll('button')].find(button => button.textContent === 'mapped-backend')
|
|
51
|
+
expect(target).toBeDefined()
|
|
52
|
+
await act(async () => target!.click())
|
|
53
|
+
expect(onNavigate).toHaveBeenLastCalledWith(expect.objectContaining({ kind: 'services', group: 'serving.knative.dev', name: 'mapped-backend' }))
|
|
54
|
+
} finally {
|
|
55
|
+
await act(async () => root.unmount())
|
|
56
|
+
resetNavigationMap()
|
|
57
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: false })
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { KnativeReferenceLink } from './KnativeReferenceLink'
|
|
1
2
|
import { Clock, Server, Container, Link2 } from 'lucide-react'
|
|
2
3
|
import { clsx } from 'clsx'
|
|
3
|
-
import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner
|
|
4
|
-
import { kindToPlural } from '../../../utils/navigation'
|
|
4
|
+
import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner } from '../../ui/drawer-components'
|
|
5
5
|
import { getKnativeConditionStatus } from '../resource-utils-knative'
|
|
6
6
|
|
|
7
7
|
interface RendererProps {
|
|
8
8
|
data: any
|
|
9
|
-
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
9
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string; group?: string }) => void
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function SinkProperty({ sink, ns, onNavigate }: { sink: any; ns: string; onNavigate?: RendererProps['onNavigate'] }) {
|
|
@@ -15,10 +15,9 @@ function SinkProperty({ sink, ns, onNavigate }: { sink: any; ns: string; onNavig
|
|
|
15
15
|
if (sink.ref) {
|
|
16
16
|
return (
|
|
17
17
|
<Property label="Sink" value={
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
namespace={sink.ref.namespace || ns}
|
|
18
|
+
<KnativeReferenceLink
|
|
19
|
+
reference={sink.ref}
|
|
20
|
+
namespace={ns}
|
|
22
21
|
onNavigate={onNavigate}
|
|
23
22
|
/>
|
|
24
23
|
} />
|
|
@@ -194,10 +193,9 @@ export function SinkBindingRenderer({ data, onNavigate }: RendererProps) {
|
|
|
194
193
|
<Property label="Subject API" value={subjectRef.apiVersion} />
|
|
195
194
|
{subjectRef.name && (
|
|
196
195
|
<Property label="Subject" value={
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
namespace={subjectRef.namespace || ns}
|
|
196
|
+
<KnativeReferenceLink
|
|
197
|
+
reference={subjectRef}
|
|
198
|
+
namespace={ns}
|
|
201
199
|
onNavigate={onNavigate}
|
|
202
200
|
/>
|
|
203
201
|
} />
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Layers, FolderTree, GitBranch } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts, ResourceLink } from '../../ui/drawer-components'
|
|
3
|
-
import { formatAge } from '../resource-utils'
|
|
4
3
|
import { GitOpsStatusBadge, ManagedResourcesList, SyncCountdown } from '../../gitops'
|
|
5
4
|
import { fluxConditionsToGitOpsStatus, parseFluxInventory, type FluxCondition } from '../../../types/gitops'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface KustomizationRendererProps {
|
|
8
8
|
data: any
|
|
@@ -175,7 +175,7 @@ export function KustomizationRenderer({ data, onNavigate }: KustomizationRendere
|
|
|
175
175
|
{status.lastHandledReconcileAt && (
|
|
176
176
|
<Property
|
|
177
177
|
label="Last Reconciled"
|
|
178
|
-
value={
|
|
178
|
+
value={<TimeValue timestamp={status.lastHandledReconcileAt} />}
|
|
179
179
|
/>
|
|
180
180
|
)}
|
|
181
181
|
</PropertyList>
|
|
@@ -2,8 +2,9 @@ import { Globe, RefreshCw } from 'lucide-react'
|
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
4
|
import { HEALTH_BADGE_COLORS } from '../../../utils/badge-colors'
|
|
5
|
-
import {
|
|
5
|
+
import { healthColors } from '../resource-utils'
|
|
6
6
|
import type { StatusBadge } from '../resource-utils'
|
|
7
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
7
8
|
|
|
8
9
|
type Nav = (ref: { kind: string; namespace: string; name: string; group?: string }) => void
|
|
9
10
|
|
|
@@ -66,7 +67,7 @@ export function KyvernoGlobalContextRenderer({ data, onNavigate }: { data: any;
|
|
|
66
67
|
</span>
|
|
67
68
|
}
|
|
68
69
|
/>
|
|
69
|
-
{refresh.at && <Property label="Last Refreshed" value={
|
|
70
|
+
{refresh.at && <Property label="Last Refreshed" value={<TimeValue timestamp={refresh.at} />} />}
|
|
70
71
|
{api?.refreshInterval && <Property label="Interval" value={api.refreshInterval} />}
|
|
71
72
|
{api?.retryLimit != null && <Property label="Retry Limit" value={String(api.retryLimit)} />}
|
|
72
73
|
</PropertyList>
|
|
@@ -3,7 +3,8 @@ import { clsx } from 'clsx'
|
|
|
3
3
|
import { ListChecks, Workflow, Boxes, FileWarning } from 'lucide-react'
|
|
4
4
|
import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
5
5
|
import { HEALTH_BADGE_COLORS, SEVERITY_BADGE } from '../../../utils/badge-colors'
|
|
6
|
-
import {
|
|
6
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
7
|
+
import { formatRelativeAgeTime } from '../../../utils/format'
|
|
7
8
|
import {
|
|
8
9
|
getKyvernoRequestType,
|
|
9
10
|
getKyvernoRequestTriggers,
|
|
@@ -155,7 +156,7 @@ export function KyvernoUpdateRequestRenderer({ data, onNavigate }: { data: any;
|
|
|
155
156
|
}
|
|
156
157
|
/>
|
|
157
158
|
{data?.metadata?.creationTimestamp && (
|
|
158
|
-
<Property label="Queued" value={
|
|
159
|
+
<Property label="Queued" value={<TimeValue timestamp={data.metadata.creationTimestamp} />} />
|
|
159
160
|
)}
|
|
160
161
|
</PropertyList>
|
|
161
162
|
{message && state.level !== 'unhealthy' && (
|
|
@@ -290,7 +291,7 @@ export function KyvernoEphemeralReportRenderer({ data, onNavigate }: { data: any
|
|
|
290
291
|
</span>
|
|
291
292
|
)}
|
|
292
293
|
{at && (
|
|
293
|
-
<span className="ml-auto shrink-0 text-theme-text-tertiary">{
|
|
294
|
+
<span className="ml-auto shrink-0 text-theme-text-tertiary">{formatRelativeAgeTime(at)}</span>
|
|
294
295
|
)}
|
|
295
296
|
</div>
|
|
296
297
|
)
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { Timer } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, AlertBanner } from '../../ui/drawer-components'
|
|
4
|
+
import { formatRelativeAgeTime } from '../../../utils/format'
|
|
4
5
|
|
|
5
6
|
interface LeaseRendererProps {
|
|
6
7
|
data: any
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
function formatRelativeTime(timestamp: string): string {
|
|
10
|
-
const diff = Date.now() - new Date(timestamp).getTime()
|
|
11
|
-
if (diff < 0) return 'just now'
|
|
12
|
-
const seconds = Math.floor(diff / 1000)
|
|
13
|
-
if (seconds < 60) return `${seconds}s ago`
|
|
14
|
-
const minutes = Math.floor(seconds / 60)
|
|
15
|
-
if (minutes < 60) return `${minutes}m ago`
|
|
16
|
-
const hours = Math.floor(minutes / 60)
|
|
17
|
-
if (hours < 24) return `${hours}h ago`
|
|
18
|
-
const days = Math.floor(hours / 24)
|
|
19
|
-
return `${days}d ago`
|
|
20
|
-
}
|
|
21
|
-
|
|
22
10
|
export function LeaseRenderer({ data }: LeaseRendererProps) {
|
|
23
11
|
const spec = data.spec || {}
|
|
24
12
|
|
|
@@ -68,7 +56,7 @@ export function LeaseRenderer({ data }: LeaseRendererProps) {
|
|
|
68
56
|
value={
|
|
69
57
|
spec.acquireTime ? (
|
|
70
58
|
<span title={new Date(spec.acquireTime).toLocaleString()}>
|
|
71
|
-
{
|
|
59
|
+
{formatRelativeAgeTime(spec.acquireTime)}
|
|
72
60
|
</span>
|
|
73
61
|
) : undefined
|
|
74
62
|
}
|
|
@@ -81,7 +69,7 @@ export function LeaseRenderer({ data }: LeaseRendererProps) {
|
|
|
81
69
|
className={clsx(renewColor)}
|
|
82
70
|
title={new Date(renewTime).toLocaleString()}
|
|
83
71
|
>
|
|
84
|
-
{
|
|
72
|
+
{formatRelativeAgeTime(renewTime)}
|
|
85
73
|
</span>
|
|
86
74
|
) : undefined
|
|
87
75
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isCrossplanePaused,
|
|
12
12
|
getComposingXRRef,
|
|
13
13
|
} from '../resource-utils-crossplane'
|
|
14
|
-
import {
|
|
14
|
+
import { kindToPluralWithGroup } from '../../../utils/navigation'
|
|
15
15
|
|
|
16
16
|
interface ManagedResourceRendererProps {
|
|
17
17
|
data: any
|
|
@@ -82,7 +82,7 @@ export function ManagedResourceRenderer({ data, onNavigate }: ManagedResourceRen
|
|
|
82
82
|
value={
|
|
83
83
|
<ResourceLink
|
|
84
84
|
name={composingXR.name}
|
|
85
|
-
kind={
|
|
85
|
+
kind={kindToPluralWithGroup(composingXR.kind, extractApiGroup(composingXR.apiVersion))}
|
|
86
86
|
namespace={namespace}
|
|
87
87
|
group={extractApiGroup(composingXR.apiVersion) || undefined}
|
|
88
88
|
onNavigate={onNavigate}
|
|
@@ -2,16 +2,17 @@ import { Info } from 'lucide-react'
|
|
|
2
2
|
import { Tooltip } from '../../ui/Tooltip'
|
|
3
3
|
|
|
4
4
|
interface MetricsUnavailableNoticeProps {
|
|
5
|
+
noUsageYet?: boolean
|
|
5
6
|
rawError?: string
|
|
6
7
|
diagnosis?: string
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export function MetricsUnavailableNotice({ rawError, diagnosis }: MetricsUnavailableNoticeProps) {
|
|
10
|
+
export function MetricsUnavailableNotice({ rawError, diagnosis, noUsageYet }: MetricsUnavailableNoticeProps) {
|
|
10
11
|
return (
|
|
11
12
|
<div className="card-inner-lg text-xs text-theme-text-tertiary">
|
|
12
13
|
<div className="flex items-center gap-1.5">
|
|
13
14
|
<span className="min-w-0 leading-5">
|
|
14
|
-
Metrics unavailable. Radar cannot read metrics.k8s.io.
|
|
15
|
+
{noUsageYet ? 'No usage yet: this Pod is not running' : 'Metrics unavailable. Radar cannot read metrics.k8s.io.'}
|
|
15
16
|
</span>
|
|
16
17
|
{rawError && (
|
|
17
18
|
<Tooltip
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createElement } from 'react'
|
|
2
|
+
import { renderToString } from 'react-dom/server'
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
4
|
+
import { NamespaceRenderer, quotaUsageRatio } from './NamespaceRenderer'
|
|
3
5
|
|
|
4
6
|
describe('quotaUsageRatio', () => {
|
|
5
7
|
// status.used is computed with quantity arithmetic, so a used pod count of
|
|
@@ -28,3 +30,68 @@ describe('quotaUsageRatio', () => {
|
|
|
28
30
|
expect(quotaUsageRatio('pods', '5', 'garbage')).toBeNull()
|
|
29
31
|
})
|
|
30
32
|
})
|
|
33
|
+
|
|
34
|
+
describe('namespace deletion diagnostics', () => {
|
|
35
|
+
afterEach(() => vi.useRealTimers())
|
|
36
|
+
|
|
37
|
+
it('shows controller blockers, namespace finalizers and time since deletion began', () => {
|
|
38
|
+
vi.useFakeTimers()
|
|
39
|
+
vi.setSystemTime(new Date('2026-10-10T12:00:00Z'))
|
|
40
|
+
const html = renderToString(createElement(NamespaceRenderer, { data: {
|
|
41
|
+
metadata: {
|
|
42
|
+
creationTimestamp: '2023-01-01T00:00:00Z',
|
|
43
|
+
deletionTimestamp: '2026-10-10T10:00:00Z',
|
|
44
|
+
finalizers: ['metadata.example.com/not-the-namespace-finalizer'],
|
|
45
|
+
},
|
|
46
|
+
spec: { finalizers: ['kubernetes'] },
|
|
47
|
+
status: { phase: 'Terminating', conditions: [
|
|
48
|
+
{ type: 'NamespaceContentRemaining', status: 'True', reason: 'SomeResourcesRemain', message: 'Some resources are remaining: configmaps. has 1 resource instances' },
|
|
49
|
+
{ type: 'NamespaceFinalizersRemaining', status: 'True', reason: 'SomeFinalizersRemain', message: 'Some content has finalizers remaining: example.com/hold in 1 resource instances' },
|
|
50
|
+
{ type: 'NamespaceDeletionDiscoveryFailure', status: 'False', reason: 'ResourcesDiscovered', message: 'All resources successfully discovered' },
|
|
51
|
+
] },
|
|
52
|
+
} }))
|
|
53
|
+
expect(html).toContain('Terminating for')
|
|
54
|
+
expect(html).toContain('>2h<')
|
|
55
|
+
expect(html).toContain('Namespace finalizers')
|
|
56
|
+
expect(html).toContain('kubernetes')
|
|
57
|
+
expect(html).not.toContain('metadata.example.com/not-the-namespace-finalizer')
|
|
58
|
+
expect(html).toContain('Conditions (3) · 2 failing')
|
|
59
|
+
expect(html).toContain('SomeResourcesRemain')
|
|
60
|
+
expect(html).toContain('configmaps. has 1 resource instances')
|
|
61
|
+
expect(html).toContain('example.com/hold in 1 resource instances')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('omits deletion duration, finalizers and empty conditions for an active namespace', () => {
|
|
65
|
+
const html = renderToString(createElement(NamespaceRenderer, { data: {
|
|
66
|
+
metadata: { creationTimestamp: '2023-01-01T00:00:00Z' },
|
|
67
|
+
spec: { finalizers: ['kubernetes'] },
|
|
68
|
+
status: { phase: 'Active' },
|
|
69
|
+
} }))
|
|
70
|
+
expect(html).toContain('Active')
|
|
71
|
+
expect(html).not.toContain('Terminating for')
|
|
72
|
+
expect(html).not.toContain('Conditions (')
|
|
73
|
+
expect(html).not.toContain('Namespace finalizers')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('does not substitute creation age when a terminating namespace lacks a deletion timestamp', () => {
|
|
77
|
+
const html = renderToString(createElement(NamespaceRenderer, { data: {
|
|
78
|
+
metadata: { creationTimestamp: '2023-01-01T00:00:00Z' },
|
|
79
|
+
status: { phase: 'Terminating', conditions: [] },
|
|
80
|
+
} }))
|
|
81
|
+
expect(html).toContain('Terminating')
|
|
82
|
+
expect(html).not.toContain('Terminating for')
|
|
83
|
+
expect(html).not.toContain('Conditions (')
|
|
84
|
+
expect(html).not.toContain('Namespace finalizers')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('retains conditions with unknown status without calling them failures', () => {
|
|
88
|
+
const html = renderToString(createElement(NamespaceRenderer, { data: {
|
|
89
|
+
status: { phase: 'Active', conditions: [
|
|
90
|
+
{ type: 'NamespaceDeletionDiscoveryFailure', status: 'Unknown', reason: 'DiscoveryPending', message: 'Waiting for discovery' },
|
|
91
|
+
] },
|
|
92
|
+
} }))
|
|
93
|
+
expect(html).toContain('Conditions (1)')
|
|
94
|
+
expect(html).toContain('Waiting for discovery')
|
|
95
|
+
expect(html).not.toContain('failing')
|
|
96
|
+
})
|
|
97
|
+
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Shield, Box, Users, Gauge } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
|
-
import { Section, PropertyList, Property, ResourceLink } from '../../ui/drawer-components'
|
|
3
|
+
import { Section, PropertyList, Property, ResourceLink, ConditionsSection } from '../../ui/drawer-components'
|
|
4
4
|
import type { RBACNamespaceResponse, RBACBindingWithSubjects, RBACSubject, ResourceRef } from '../../../types'
|
|
5
|
+
import { formatAge } from '../resource-utils'
|
|
5
6
|
import { rbacKindBadgeClass } from '../../../utils/rbac-badges'
|
|
6
7
|
import { RBACErrorSection } from './RBACErrorSection'
|
|
7
8
|
import { NamespaceLimitRangesSection } from './LimitRangeRenderer'
|
|
@@ -70,12 +71,16 @@ export function NamespaceRenderer({ data, rbacData, rbacLoading, rbacError, quot
|
|
|
70
71
|
)}>{phase}</span>
|
|
71
72
|
) : undefined
|
|
72
73
|
} />
|
|
74
|
+
{metadata.deletionTimestamp && <Property label="Terminating for" value={formatAge(metadata.deletionTimestamp)} />}
|
|
75
|
+
{metadata.deletionTimestamp && data.spec?.finalizers?.length > 0 && <Property label="Namespace finalizers" value={data.spec.finalizers.join(', ')} />}
|
|
73
76
|
{istioInjection && <Property label="Istio injection" value={istioInjection} />}
|
|
74
77
|
{linkerdInjection && <Property label="Linkerd injection" value={linkerdInjection} />}
|
|
75
78
|
{managedBy && <Property label="Managed by" value={managedBy} />}
|
|
76
79
|
</PropertyList>
|
|
77
80
|
</Section>
|
|
78
81
|
|
|
82
|
+
<ConditionsSection conditions={status.conditions} defaultExpanded />
|
|
83
|
+
|
|
79
84
|
{/* ResourceQuota usage — only when host wired the fetch. */}
|
|
80
85
|
{(quotaError || (quotaData != null && quotaData.length > 0)) && (
|
|
81
86
|
<NamespaceQuotaSection quotas={quotaData ?? []} error={quotaError ?? null} />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { renderToString } from 'react-dom/server'
|
|
2
|
+
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
|
|
3
3
|
import { NodeRenderer } from './NodeRenderer'
|
|
4
4
|
|
|
5
5
|
const node = {
|
|
@@ -101,3 +101,83 @@ describe('NodeRenderer metrics', () => {
|
|
|
101
101
|
expect(html).toContain('CPU')
|
|
102
102
|
})
|
|
103
103
|
})
|
|
104
|
+
|
|
105
|
+
describe('NodeRenderer removal advice', () => {
|
|
106
|
+
const removal = {
|
|
107
|
+
...node,
|
|
108
|
+
spec: { unschedulable: true, taints: [{ key: 'ToBeDeletedByClusterAutoscaler', value: String(Math.floor(Date.now() / 1000) - 60), effect: 'NoSchedule' }] },
|
|
109
|
+
status: { ...node.status, conditions: [{ type: 'Ready', status: 'Unknown', lastTransitionTime: new Date(Date.now() - 1000).toISOString(), message: 'Kubelet stopped posting' }] },
|
|
110
|
+
}
|
|
111
|
+
it('does not give an autoscaler removal an error or uncordon advice', () => {
|
|
112
|
+
const html = renderToStaticMarkup(<NodeRenderer data={removal} />)
|
|
113
|
+
expect(html).toContain('Removing (cluster autoscaler)')
|
|
114
|
+
expect(html).not.toContain('Issues Detected')
|
|
115
|
+
expect(html).not.toContain('Uncordon')
|
|
116
|
+
expect(html).toContain('Pod and disruption-budget checks are not available')
|
|
117
|
+
})
|
|
118
|
+
it('does not assert who caused a bare cordon', () => {
|
|
119
|
+
const html = renderToStaticMarkup(<NodeRenderer data={{ ...node, spec: { unschedulable: true } }} />)
|
|
120
|
+
expect(html).toContain('Cordoned')
|
|
121
|
+
expect(html).toContain('who cordoned it is not recorded')
|
|
122
|
+
expect(html).not.toContain('Uncordon to resume')
|
|
123
|
+
})
|
|
124
|
+
it('keeps observed readiness False neutral when it follows the removal start', () => {
|
|
125
|
+
const html = renderToStaticMarkup(<NodeRenderer data={{ ...removal, status: { ...removal.status, conditions: [{ ...removal.status.conditions[0], status: 'False' }] } }} />)
|
|
126
|
+
expect(html).toContain('Removing (cluster autoscaler)')
|
|
127
|
+
expect(html).toContain('Kubelet stopped posting')
|
|
128
|
+
expect(html).not.toContain('1 failing')
|
|
129
|
+
expect(html).not.toContain('Issues Detected')
|
|
130
|
+
})
|
|
131
|
+
it('shows named possible PDB blockers and terminating pods without invented progress', () => {
|
|
132
|
+
const html = renderToStaticMarkup(<NodeRenderer data={removal} removalPlan={{
|
|
133
|
+
node: 'kind-worker', generatedAt: new Date().toISOString(), estimate: true,
|
|
134
|
+
options: { ignoreDaemonSets: true, deleteEmptyDirData: true, force: true },
|
|
135
|
+
summary: { evict: 1, skip: 0, mayBlock: 1 }, pdbsEvaluated: true,
|
|
136
|
+
pods: [
|
|
137
|
+
{ namespace: 'shop', name: 'web', outcome: 'may-block', reason: 'PodDisruptionBudget shop/web allows no disruptions', pdb: 'shop/web', pdbChecked: true, emptyDir: false, terminating: false },
|
|
138
|
+
{ namespace: 'shop', name: 'old', outcome: 'evict', reason: 'already terminating', pdbChecked: false, emptyDir: false, terminating: true },
|
|
139
|
+
],
|
|
140
|
+
}} />)
|
|
141
|
+
expect(html).toContain('2 pods in the current snapshot')
|
|
142
|
+
expect(html).toContain('1 terminating')
|
|
143
|
+
expect(html).toContain('shop/web')
|
|
144
|
+
expect(html).toContain('may block removal')
|
|
145
|
+
expect(html).not.toContain('evicted')
|
|
146
|
+
})
|
|
147
|
+
it('uses warning tone for independent network failure during removal', () => {
|
|
148
|
+
const html = renderToStaticMarkup(<NodeRenderer data={{ ...removal, status: { conditions: [
|
|
149
|
+
...removal.status.conditions,
|
|
150
|
+
{ type: 'NetworkUnavailable', status: 'True', lastTransitionTime: new Date(Date.now() - 180_000).toISOString() },
|
|
151
|
+
] } }} />)
|
|
152
|
+
expect(html).toContain('Removing (cluster autoscaler) · Network unavailable')
|
|
153
|
+
expect(html).toContain('bg-amber-50')
|
|
154
|
+
expect(html).not.toContain('rounded-lg bg-red-50')
|
|
155
|
+
expect(html).not.toContain('rounded-lg bg-sky-50')
|
|
156
|
+
})
|
|
157
|
+
it('describes skip outcomes as drain estimates', () => {
|
|
158
|
+
const html = renderToStaticMarkup(<NodeRenderer data={removal} removalPlan={{
|
|
159
|
+
node: 'kind-worker', generatedAt: new Date().toISOString(), estimate: true,
|
|
160
|
+
options: { ignoreDaemonSets: true, deleteEmptyDirData: true, force: true },
|
|
161
|
+
summary: { evict: 0, skip: 1, mayBlock: 0 }, pdbsEvaluated: true,
|
|
162
|
+
pods: [{ namespace: 'shop', name: 'daemon', outcome: 'skip', reason: 'DaemonSet', pdbChecked: false, emptyDir: false, terminating: false }],
|
|
163
|
+
}} />)
|
|
164
|
+
expect(html).toContain('Skipped by drain estimate')
|
|
165
|
+
expect(html).toContain('1 skipped by drain estimate')
|
|
166
|
+
expect(html).not.toContain('Retained')
|
|
167
|
+
})
|
|
168
|
+
it('reports unavailable drain evidence as unavailable', () => {
|
|
169
|
+
const html = renderToStaticMarkup(<NodeRenderer data={removal} removalPlanError="forbidden: cannot list pods" />)
|
|
170
|
+
expect(html).toContain('Drain details unavailable')
|
|
171
|
+
expect(html).toContain('forbidden: cannot list pods')
|
|
172
|
+
expect(html).not.toContain('0 pods')
|
|
173
|
+
})
|
|
174
|
+
it('does not present a cached snapshot as current after a refresh fails', () => {
|
|
175
|
+
const html = renderToStaticMarkup(<NodeRenderer data={removal} removalPlanError="forbidden: cannot list pods" removalPlan={{
|
|
176
|
+
node: 'kind-worker', generatedAt: new Date().toISOString(), estimate: true,
|
|
177
|
+
options: { ignoreDaemonSets: true, deleteEmptyDirData: true, force: true },
|
|
178
|
+
summary: { evict: 0, skip: 0, mayBlock: 0 }, pdbsEvaluated: true, pods: [],
|
|
179
|
+
}} />)
|
|
180
|
+
expect(html).toContain('Drain details unavailable')
|
|
181
|
+
expect(html).not.toContain('0 pods in the current snapshot')
|
|
182
|
+
})
|
|
183
|
+
})
|