@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,8 +1,8 @@
|
|
|
1
1
|
import { Activity, Boxes, Server } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, AlertBanner, ConditionsSection } from '../../ui/drawer-components'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { canaryStepTemplateRefs } from './RolloutRenderer'
|
|
5
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
6
6
|
|
|
7
7
|
interface ExperimentRendererProps {
|
|
8
8
|
data: any
|
|
@@ -72,7 +72,7 @@ export function ExperimentRenderer({ data, onNavigate }: ExperimentRendererProps
|
|
|
72
72
|
<Property label="Phase" value={<span className={clsx('badge', phaseClass(phase))}>{phase}</span>} />
|
|
73
73
|
<Property label="Duration" value={spec.duration} />
|
|
74
74
|
<Property label="Message" value={status.message} />
|
|
75
|
-
<Property label="Available At" value={status.availableAt ?
|
|
75
|
+
<Property label="Available At" value={status.availableAt ? <TimeValue timestamp={status.availableAt} /> : undefined} />
|
|
76
76
|
{ownerRollout && (
|
|
77
77
|
<Property
|
|
78
78
|
label="Rollout"
|
|
@@ -3,10 +3,10 @@ import { KeyRound } from 'lucide-react'
|
|
|
3
3
|
import { clsx } from 'clsx'
|
|
4
4
|
import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
5
5
|
import { Collapse, CollapseChevron, useDisclosure } from '../../ui/Collapse'
|
|
6
|
-
import { formatAge } from '../resource-utils'
|
|
7
6
|
import { SEVERITY_BADGE_COLORS, TrivyAlertBanner, formatTrivyImage } from './trivy-shared'
|
|
8
7
|
import { pluralize } from '../../../utils/pluralize'
|
|
9
8
|
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
9
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
10
10
|
|
|
11
11
|
interface ExposedSecretReportRendererProps {
|
|
12
12
|
data: any
|
|
@@ -45,7 +45,7 @@ export function ExposedSecretReportRenderer({ data }: ExposedSecretReportRendere
|
|
|
45
45
|
<Property label="Container" value={containerName} />
|
|
46
46
|
<Property label="Image" value={image} />
|
|
47
47
|
<Property label="Scanner" value={scanner.name ? `${scanner.name} ${scanner.version || ''}`.trim() : '-'} />
|
|
48
|
-
<Property label="Scanned" value={
|
|
48
|
+
<Property label="Scanned" value={<TimeValue timestamp={report.updateTimestamp} />} />
|
|
49
49
|
</PropertyList>
|
|
50
50
|
</Section>
|
|
51
51
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { KeyRound, RefreshCw, CloudCog } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown } from '../../ui/drawer-components'
|
|
3
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
3
4
|
import {
|
|
4
5
|
getExternalSecretStatus,
|
|
5
6
|
getExternalSecretStore,
|
|
6
7
|
getExternalSecretRefreshInterval,
|
|
7
|
-
getExternalSecretLastSync,
|
|
8
8
|
getExternalSecretTargetName,
|
|
9
9
|
getExternalSecretTargetCreationPolicy,
|
|
10
10
|
getExternalSecretTargetDeletionPolicy,
|
|
@@ -47,7 +47,7 @@ export function ExternalSecretRenderer({ data, onNavigate }: ExternalSecretRende
|
|
|
47
47
|
<Section title="Sync Status" icon={RefreshCw} defaultExpanded>
|
|
48
48
|
<PropertyList>
|
|
49
49
|
<Property label="Status" value={esStatus.text} />
|
|
50
|
-
<Property label="Last Sync" value={
|
|
50
|
+
<Property label="Last Sync" value={<TimeValue timestamp={data.status?.refreshTime} />} />
|
|
51
51
|
<Property label="Refresh Interval" value={getExternalSecretRefreshInterval(data)} />
|
|
52
52
|
<Property label="Target Secret" value={(() => {
|
|
53
53
|
const targetName = getExternalSecretTargetName(data)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Package, Settings, CheckCircle2, History, FileCode2, GitBranch } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { GitOpsStatusBadge, SyncCountdown } from '../../gitops'
|
|
6
5
|
import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
|
|
6
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
7
7
|
|
|
8
8
|
interface FluxHelmReleaseRendererProps {
|
|
9
9
|
data: any
|
|
@@ -249,13 +249,13 @@ export function FluxHelmReleaseRenderer({ data, onNavigate }: FluxHelmReleaseRen
|
|
|
249
249
|
{lastRelease.firstDeployed && (
|
|
250
250
|
<Property
|
|
251
251
|
label="First Deployed"
|
|
252
|
-
value={
|
|
252
|
+
value={<TimeValue timestamp={lastRelease.firstDeployed} />}
|
|
253
253
|
/>
|
|
254
254
|
)}
|
|
255
255
|
{lastRelease.lastDeployed && (
|
|
256
256
|
<Property
|
|
257
257
|
label="Last Deployed"
|
|
258
|
-
value={
|
|
258
|
+
value={<TimeValue timestamp={lastRelease.lastDeployed} />}
|
|
259
259
|
/>
|
|
260
260
|
)}
|
|
261
261
|
</PropertyList>
|
|
@@ -319,7 +319,7 @@ export function FluxHelmReleaseRenderer({ data, onNavigate }: FluxHelmReleaseRen
|
|
|
319
319
|
{status.lastHandledReconcileAt && (
|
|
320
320
|
<Property
|
|
321
321
|
label="Last Reconciled"
|
|
322
|
-
value={
|
|
322
|
+
value={<TimeValue timestamp={status.lastHandledReconcileAt} />}
|
|
323
323
|
/>
|
|
324
324
|
)}
|
|
325
325
|
</PropertyList>
|
|
@@ -2,6 +2,7 @@ import { Globe, ArrowRight, Network, Filter } from 'lucide-react'
|
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceRefBadge, useOperationalIssuesShown } from '../../ui/drawer-components'
|
|
3
3
|
import { Badge } from '../../ui/Badge'
|
|
4
4
|
import type { ResourceRef } from '../../../types'
|
|
5
|
+
import { gatewayBackendResourceRef, gatewayParentResourceRef } from '../../../utils/gateway-references'
|
|
5
6
|
|
|
6
7
|
interface GRPCRouteRendererProps {
|
|
7
8
|
data: any
|
|
@@ -32,22 +33,8 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
32
33
|
? parentStatuses[0].conditions
|
|
33
34
|
: undefined
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
kind: 'Gateway',
|
|
38
|
-
namespace: ref.namespace || routeNs,
|
|
39
|
-
name: ref.name,
|
|
40
|
-
group: 'gateway.networking.k8s.io',
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function toServiceRef(backend: any): ResourceRef {
|
|
45
|
-
return {
|
|
46
|
-
kind: 'Service',
|
|
47
|
-
namespace: backend.namespace || routeNs,
|
|
48
|
-
name: backend.name,
|
|
49
|
-
}
|
|
50
|
-
}
|
|
36
|
+
const toParentRef = (ref: any) => gatewayParentResourceRef(ref, routeNs)
|
|
37
|
+
const toBackendRef = (ref: any) => gatewayBackendResourceRef(ref, routeNs)
|
|
51
38
|
|
|
52
39
|
return (
|
|
53
40
|
<>
|
|
@@ -59,8 +46,8 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
59
46
|
const cond = (p.conditions || []).find((c: any) => c.type === 'Accepted' && c.status === 'False')
|
|
60
47
|
const gwName = p.parentRef?.name || 'unknown'
|
|
61
48
|
return cond?.reason
|
|
62
|
-
? `
|
|
63
|
-
: `
|
|
49
|
+
? `Parent "${gwName}": ${cond.reason}${cond.message ? ' — ' + cond.message : ''}`
|
|
50
|
+
: `Parent "${gwName}" has not accepted this route.`
|
|
64
51
|
}).join('; ')}
|
|
65
52
|
/>
|
|
66
53
|
)}
|
|
@@ -90,12 +77,12 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
90
77
|
}
|
|
91
78
|
/>
|
|
92
79
|
<Property
|
|
93
|
-
label="
|
|
80
|
+
label="Parents"
|
|
94
81
|
value={
|
|
95
82
|
parentRefs.length > 0 ? (
|
|
96
83
|
<div className="flex flex-wrap gap-1">
|
|
97
84
|
{parentRefs.map((ref: any, i: number) => (
|
|
98
|
-
<ResourceRefBadge key={`${ref.namespace || ''}-${ref.name}-${i}`} resourceRef={
|
|
85
|
+
<ResourceRefBadge key={`${ref.namespace || ''}-${ref.name}-${i}`} resourceRef={toParentRef(ref)} onClick={onNavigate} />
|
|
99
86
|
))}
|
|
100
87
|
</div>
|
|
101
88
|
) : 'None'
|
|
@@ -129,7 +116,7 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
129
116
|
<div className="text-xs text-theme-text-secondary flex flex-wrap items-center gap-1.5">
|
|
130
117
|
<span className="italic text-theme-text-tertiary">all</span>
|
|
131
118
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
132
|
-
<ResourceRefBadge resourceRef={
|
|
119
|
+
<ResourceRefBadge resourceRef={toBackendRef(backendRefs[0])} onClick={onNavigate} />
|
|
133
120
|
{backendRefs[0].port && <span className="text-theme-text-tertiary">:{backendRefs[0].port}</span>}
|
|
134
121
|
</div>
|
|
135
122
|
)}
|
|
@@ -141,7 +128,7 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
141
128
|
return (
|
|
142
129
|
<div key={bi} className="text-xs text-theme-text-secondary flex items-center gap-1.5 pl-1">
|
|
143
130
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
144
|
-
<ResourceRefBadge resourceRef={
|
|
131
|
+
<ResourceRefBadge resourceRef={toBackendRef(b)} onClick={onNavigate} />
|
|
145
132
|
{b.port && <span className="text-theme-text-tertiary">:{b.port}</span>}
|
|
146
133
|
{pct !== null && <span className="text-theme-text-tertiary text-[10px]">{pct}%</span>}
|
|
147
134
|
</div>
|
|
@@ -176,7 +163,7 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
176
163
|
{matchIdx === 0 && backendRefs.length === 1 && (
|
|
177
164
|
<>
|
|
178
165
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
179
|
-
<ResourceRefBadge resourceRef={
|
|
166
|
+
<ResourceRefBadge resourceRef={toBackendRef(backendRefs[0])} onClick={onNavigate} />
|
|
180
167
|
{backendRefs[0].port && <span className="text-theme-text-tertiary">:{backendRefs[0].port}</span>}
|
|
181
168
|
</>
|
|
182
169
|
)}
|
|
@@ -189,7 +176,7 @@ export function GRPCRouteRenderer({ data, onNavigate }: GRPCRouteRendererProps)
|
|
|
189
176
|
return (
|
|
190
177
|
<div key={bi} className="text-xs text-theme-text-secondary flex items-center gap-1.5 pl-1">
|
|
191
178
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
192
|
-
<ResourceRefBadge resourceRef={
|
|
179
|
+
<ResourceRefBadge resourceRef={toBackendRef(b)} onClick={onNavigate} />
|
|
193
180
|
{b.port && <span className="text-theme-text-tertiary">:{b.port}</span>}
|
|
194
181
|
{pct !== null && <span className="text-theme-text-tertiary text-[10px]">{pct}%</span>}
|
|
195
182
|
</div>
|
|
@@ -0,0 +1,63 @@
|
|
|
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 { HTTPRouteRenderer } from './HTTPRouteRenderer'
|
|
6
|
+
import { GRPCRouteRenderer } from './GRPCRouteRenderer'
|
|
7
|
+
import { SimpleRouteRenderer } from './SimpleRouteRenderer'
|
|
8
|
+
|
|
9
|
+
const backend = { name: 'custom-backend', kind: 'Widget', group: 'relationships.radar.test', namespace: 'backends' }
|
|
10
|
+
const parent = { name: 'mesh-parent', kind: 'Service', group: '', namespace: 'mesh' }
|
|
11
|
+
|
|
12
|
+
function routeData(kind: string) {
|
|
13
|
+
return {
|
|
14
|
+
metadata: { name: 'route', namespace: 'app' },
|
|
15
|
+
status: { parents: [{ parentRef: parent, conditions: [{ type: 'Accepted', status: 'False', reason: 'Rejected' }] }] },
|
|
16
|
+
spec: {
|
|
17
|
+
parentRefs: [parent],
|
|
18
|
+
rules: [{
|
|
19
|
+
backendRefs: [backend, { name: 'default-service', port: 8080 }],
|
|
20
|
+
...(kind === 'HTTPRoute' ? { filters: [{ type: 'RequestMirror', requestMirror: { backendRef: { ...backend, name: 'mirror' } } }] } : {}),
|
|
21
|
+
}],
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('Gateway route reference navigation', () => {
|
|
27
|
+
it.each(['HTTPRoute', 'GRPCRoute', 'TCPRoute', 'TLSRoute'] as const)('preserves backend and parent identities in %s', async kind => {
|
|
28
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
|
29
|
+
const container = document.createElement('div')
|
|
30
|
+
const root = createRoot(container)
|
|
31
|
+
const onNavigate = vi.fn()
|
|
32
|
+
const data = routeData(kind)
|
|
33
|
+
const click = async (name: string) => {
|
|
34
|
+
const button = [...container.querySelectorAll('button')].find(button => button.textContent?.includes(name))
|
|
35
|
+
expect(button, `button ${name}`).toBeDefined()
|
|
36
|
+
await act(async () => button!.click())
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await act(async () => root.render(
|
|
40
|
+
kind === 'HTTPRoute' ? <HTTPRouteRenderer data={data} onNavigate={onNavigate} />
|
|
41
|
+
: kind === 'GRPCRoute' ? <GRPCRouteRenderer data={data} onNavigate={onNavigate} />
|
|
42
|
+
: <SimpleRouteRenderer kind={kind} data={data} onNavigate={onNavigate} />,
|
|
43
|
+
))
|
|
44
|
+
expect(container.textContent).toContain('Parents')
|
|
45
|
+
expect(container.textContent).toContain('Parent "mesh-parent"')
|
|
46
|
+
expect(container.textContent).not.toContain('Gateway "mesh-parent"')
|
|
47
|
+
|
|
48
|
+
await click('custom-backend')
|
|
49
|
+
expect(onNavigate).toHaveBeenLastCalledWith(backend)
|
|
50
|
+
await click('default-service')
|
|
51
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ kind: 'Service', group: '', namespace: 'app', name: 'default-service' })
|
|
52
|
+
await click('mesh-parent')
|
|
53
|
+
expect(onNavigate).toHaveBeenLastCalledWith(parent)
|
|
54
|
+
if (kind === 'HTTPRoute') {
|
|
55
|
+
await click('mirror')
|
|
56
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ ...backend, name: 'mirror' })
|
|
57
|
+
}
|
|
58
|
+
} finally {
|
|
59
|
+
await act(async () => root.unmount())
|
|
60
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: false })
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { GitBranch, FolderGit, CheckCircle2 } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts } from '../../ui/drawer-components'
|
|
3
|
-
import { formatAge } from '../resource-utils'
|
|
4
3
|
import { formatBytes } from '../../../utils/format'
|
|
5
4
|
import { GitOpsStatusBadge, SyncCountdown } from '../../gitops'
|
|
6
5
|
import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
|
|
6
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
7
7
|
|
|
8
8
|
interface GitRepositoryRendererProps {
|
|
9
9
|
data: any
|
|
@@ -87,7 +87,7 @@ export function GitRepositoryRenderer({ data }: GitRepositoryRendererProps) {
|
|
|
87
87
|
<Property label="Digest" value={artifact.digest} />
|
|
88
88
|
<Property
|
|
89
89
|
label="Last Updated"
|
|
90
|
-
value={
|
|
90
|
+
value={<TimeValue timestamp={artifact.lastUpdateTime} />}
|
|
91
91
|
/>
|
|
92
92
|
{artifact.size && (
|
|
93
93
|
<Property label="Size" value={formatBytes(artifact.size)} />
|
|
@@ -104,7 +104,7 @@ export function GitRepositoryRenderer({ data }: GitRepositoryRendererProps) {
|
|
|
104
104
|
{status.lastHandledReconcileAt && (
|
|
105
105
|
<Property
|
|
106
106
|
label="Last Reconciled"
|
|
107
|
-
value={
|
|
107
|
+
value={<TimeValue timestamp={status.lastHandledReconcileAt} />}
|
|
108
108
|
/>
|
|
109
109
|
)}
|
|
110
110
|
</PropertyList>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
import { Activity, Cpu } from 'lucide-react'
|
|
3
3
|
import { clsx } from 'clsx'
|
|
4
|
-
import { Section, PropertyList, Property, ConditionsSection, ResourceLink, type ConditionTone } from '../../ui/drawer-components'
|
|
4
|
+
import { Section, PropertyList, Property, ConditionsSection, ResourceLink, LabelSelectorDisplay, type ConditionTone } from '../../ui/drawer-components'
|
|
5
5
|
import { Badge } from '../../ui/Badge'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { objectReferenceToResourceRef, apiVersionToGroup } from '../../../utils/navigation'
|
|
7
|
+
import type { ResourceRef } from '../../../types'
|
|
8
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
8
9
|
import { HPADiagnosisSummary } from '../HPADiagnosisSummary'
|
|
9
10
|
import type { HPADiagnosis } from '../../../types'
|
|
10
11
|
|
|
11
12
|
interface HPARendererProps {
|
|
12
13
|
data: any
|
|
13
|
-
onNavigate?: (ref:
|
|
14
|
+
onNavigate?: (ref: ResourceRef) => void
|
|
14
15
|
hpaDiagnosis?: HPADiagnosis
|
|
15
16
|
/** Optional host-provided section rendered after Conditions — used to inject Prometheus-backed charts. */
|
|
16
17
|
extraSections?: ReactNode
|
|
@@ -37,6 +38,11 @@ export function HPARenderer({ data, onNavigate, hpaDiagnosis, extraSections }: H
|
|
|
37
38
|
const status = data.status || {}
|
|
38
39
|
const spec = data.spec || {}
|
|
39
40
|
const metrics = status.currentMetrics || []
|
|
41
|
+
const namespace = data.metadata?.namespace
|
|
42
|
+
const targetRef = spec.scaleTargetRef || {}
|
|
43
|
+
const target = objectReferenceToResourceRef({ ...targetRef, namespace })
|
|
44
|
+
const hasDiagnosisMetrics = !!hpaDiagnosis?.metrics?.length
|
|
45
|
+
const objectMetrics = (spec.metrics || []).filter((metric: any) => metric.type === 'Object' && metric.object)
|
|
40
46
|
|
|
41
47
|
return (
|
|
42
48
|
<>
|
|
@@ -49,32 +55,26 @@ export function HPARenderer({ data, onNavigate, hpaDiagnosis, extraSections }: H
|
|
|
49
55
|
<Section title="Scaling" icon={Cpu}>
|
|
50
56
|
<PropertyList>
|
|
51
57
|
<Property label="Target" value={
|
|
52
|
-
|
|
53
|
-
<ResourceLink
|
|
54
|
-
|
|
55
|
-
kind={kindToPlural(spec.scaleTargetRef.kind || 'Deployment')}
|
|
56
|
-
namespace={data.metadata?.namespace || ''}
|
|
57
|
-
label={`${spec.scaleTargetRef.kind}/${spec.scaleTargetRef.name}`}
|
|
58
|
-
onNavigate={onNavigate}
|
|
59
|
-
/>
|
|
60
|
-
) : undefined
|
|
58
|
+
targetRef.name ? (target ? (
|
|
59
|
+
<ResourceLink {...target} label={`${targetRef.kind}/${targetRef.name}`} onNavigate={onNavigate} />
|
|
60
|
+
) : `${targetRef.kind || 'Unknown kind'}/${targetRef.name}`) : undefined
|
|
61
61
|
} />
|
|
62
62
|
<Property label="Current" value={status.currentReplicas} />
|
|
63
63
|
<Property label="Desired" value={status.desiredReplicas} />
|
|
64
64
|
<Property label="Min" value={spec.minReplicas ?? 1} />
|
|
65
65
|
<Property label="Max" value={spec.maxReplicas} />
|
|
66
|
-
{status.lastScaleTime && <Property label="Last Scale" value={
|
|
66
|
+
{status.lastScaleTime && <Property label="Last Scale" value={<TimeValue timestamp={status.lastScaleTime} />} />}
|
|
67
67
|
</PropertyList>
|
|
68
68
|
</Section>
|
|
69
69
|
|
|
70
70
|
{hpaDiagnosis?.metrics && hpaDiagnosis.metrics.length > 0 && (
|
|
71
71
|
<Section title="Metrics" defaultExpanded>
|
|
72
72
|
<div className="space-y-2">
|
|
73
|
-
{hpaDiagnosis.metrics.map((metric) => (
|
|
74
|
-
<div key={`${metric.type}-${metric.name}`} className="card-inner">
|
|
73
|
+
{hpaDiagnosis.metrics.map((metric, index) => (
|
|
74
|
+
<div key={`${metric.type}-${metric.name}-${index}`} className="card-inner">
|
|
75
75
|
<div className="flex flex-wrap items-center justify-between gap-2 text-sm">
|
|
76
76
|
<div>
|
|
77
|
-
<div className="font-medium text-theme-text-primary">{metric.name}</div>
|
|
77
|
+
<div className="font-medium text-theme-text-primary break-words">{spec.metrics?.[index]?.type === 'Object' ? spec.metrics[index].object.metric.name : metric.name}</div>
|
|
78
78
|
<div className="text-xs text-theme-text-tertiary">{metric.type}</div>
|
|
79
79
|
</div>
|
|
80
80
|
<div className="flex flex-wrap items-center gap-2 text-xs text-theme-text-secondary">
|
|
@@ -85,6 +85,11 @@ export function HPARenderer({ data, onNavigate, hpaDiagnosis, extraSections }: H
|
|
|
85
85
|
</Badge>
|
|
86
86
|
</div>
|
|
87
87
|
</div>
|
|
88
|
+
{spec.metrics?.[index]?.type === 'Object' && (
|
|
89
|
+
<div className="mt-2">
|
|
90
|
+
<HPAObjectMetricSource object={spec.metrics[index].object} namespace={namespace} onNavigate={onNavigate} />
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
88
93
|
</div>
|
|
89
94
|
))}
|
|
90
95
|
</div>
|
|
@@ -121,9 +126,44 @@ export function HPARenderer({ data, onNavigate, hpaDiagnosis, extraSections }: H
|
|
|
121
126
|
</Section>
|
|
122
127
|
)}
|
|
123
128
|
|
|
129
|
+
{!hasDiagnosisMetrics && objectMetrics.length > 0 && (
|
|
130
|
+
<Section title="Object metric sources" defaultExpanded>
|
|
131
|
+
<div className="space-y-3">
|
|
132
|
+
{objectMetrics.map((metric: any, i: number) => (
|
|
133
|
+
<div key={`${metric.object.metric.name}-${i}`} className="card-inner">
|
|
134
|
+
<HPAObjectMetricSource object={metric.object} namespace={namespace} onNavigate={onNavigate} showMetric />
|
|
135
|
+
</div>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
</Section>
|
|
139
|
+
)}
|
|
140
|
+
|
|
124
141
|
<ConditionsSection conditions={status.conditions} getConditionTone={hpaConditionTone} />
|
|
125
142
|
|
|
126
143
|
{extraSections}
|
|
127
144
|
</>
|
|
128
145
|
)
|
|
129
146
|
}
|
|
147
|
+
|
|
148
|
+
function HPAObjectMetricSource({ object, namespace, onNavigate, showMetric = false }: {
|
|
149
|
+
object: any
|
|
150
|
+
namespace?: string
|
|
151
|
+
onNavigate?: (ref: ResourceRef) => void
|
|
152
|
+
showMetric?: boolean
|
|
153
|
+
}) {
|
|
154
|
+
const described = object.describedObject || {}
|
|
155
|
+
const namespaceMetric = described.kind === 'Namespace' && apiVersionToGroup(described.apiVersion) === ''
|
|
156
|
+
const name = namespaceMetric ? namespace : described.name
|
|
157
|
+
const source = objectReferenceToResourceRef({ ...described, name, namespace })
|
|
158
|
+
return (
|
|
159
|
+
<PropertyList>
|
|
160
|
+
{showMetric && <Property label="Metric" value={object.metric.name} />}
|
|
161
|
+
<Property label="Object" value={source ? (
|
|
162
|
+
<ResourceLink {...source} label={`${described.kind}/${name}`} onNavigate={onNavigate} />
|
|
163
|
+
) : described.name ? `${described.kind || 'Unknown kind'}/${described.name}` : undefined} />
|
|
164
|
+
{source?.group && <Property label="API Group" value={source.group} />}
|
|
165
|
+
{object.metric.selector && <Property label="Selector" value={<LabelSelectorDisplay selector={object.metric.selector} />} />}
|
|
166
|
+
{namespaceMetric && <Property label="Scope" value="This HPA's namespace" />}
|
|
167
|
+
</PropertyList>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
@@ -2,6 +2,7 @@ import { Globe, ArrowRight, Network, Filter } from 'lucide-react'
|
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceRefBadge, useOperationalIssuesShown } from '../../ui/drawer-components'
|
|
3
3
|
import { Badge } from '../../ui/Badge'
|
|
4
4
|
import type { ResourceRef } from '../../../types'
|
|
5
|
+
import { gatewayBackendResourceRef, gatewayParentResourceRef } from '../../../utils/gateway-references'
|
|
5
6
|
|
|
6
7
|
interface HTTPRouteRendererProps {
|
|
7
8
|
data: any
|
|
@@ -32,22 +33,8 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
32
33
|
? parentStatuses[0].conditions
|
|
33
34
|
: undefined
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
kind: 'Gateway',
|
|
38
|
-
namespace: ref.namespace || routeNs,
|
|
39
|
-
name: ref.name,
|
|
40
|
-
group: 'gateway.networking.k8s.io',
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function toServiceRef(backend: any): ResourceRef {
|
|
45
|
-
return {
|
|
46
|
-
kind: 'Service',
|
|
47
|
-
namespace: backend.namespace || routeNs,
|
|
48
|
-
name: backend.name,
|
|
49
|
-
}
|
|
50
|
-
}
|
|
36
|
+
const toParentRef = (ref: any) => gatewayParentResourceRef(ref, routeNs)
|
|
37
|
+
const toBackendRef = (ref: any) => gatewayBackendResourceRef(ref, routeNs)
|
|
51
38
|
|
|
52
39
|
return (
|
|
53
40
|
<>
|
|
@@ -59,8 +46,8 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
59
46
|
const cond = (p.conditions || []).find((c: any) => c.type === 'Accepted' && c.status === 'False')
|
|
60
47
|
const gwName = p.parentRef?.name || 'unknown'
|
|
61
48
|
return cond?.reason
|
|
62
|
-
? `
|
|
63
|
-
: `
|
|
49
|
+
? `Parent "${gwName}": ${cond.reason}${cond.message ? ' — ' + cond.message : ''}`
|
|
50
|
+
: `Parent "${gwName}" has not accepted this route.`
|
|
64
51
|
}).join('; ')}
|
|
65
52
|
/>
|
|
66
53
|
)}
|
|
@@ -90,12 +77,12 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
90
77
|
}
|
|
91
78
|
/>
|
|
92
79
|
<Property
|
|
93
|
-
label="
|
|
80
|
+
label="Parents"
|
|
94
81
|
value={
|
|
95
82
|
parentRefs.length > 0 ? (
|
|
96
83
|
<div className="flex flex-wrap gap-1">
|
|
97
84
|
{parentRefs.map((ref: any, i: number) => (
|
|
98
|
-
<ResourceRefBadge key={`${ref.namespace || ''}-${ref.name}-${i}`} resourceRef={
|
|
85
|
+
<ResourceRefBadge key={`${ref.namespace || ''}-${ref.name}-${i}`} resourceRef={toParentRef(ref)} onClick={onNavigate} />
|
|
99
86
|
))}
|
|
100
87
|
</div>
|
|
101
88
|
) : 'None'
|
|
@@ -129,7 +116,7 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
129
116
|
<div className="text-xs text-theme-text-secondary flex flex-wrap items-center gap-1.5">
|
|
130
117
|
<span className="italic text-theme-text-tertiary">all</span>
|
|
131
118
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
132
|
-
<ResourceRefBadge resourceRef={
|
|
119
|
+
<ResourceRefBadge resourceRef={toBackendRef(backendRefs[0])} onClick={onNavigate} />
|
|
133
120
|
{backendRefs[0].port && <span className="text-theme-text-tertiary">:{backendRefs[0].port}</span>}
|
|
134
121
|
</div>
|
|
135
122
|
)}
|
|
@@ -141,7 +128,7 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
141
128
|
return (
|
|
142
129
|
<div key={bi} className="text-xs text-theme-text-secondary flex items-center gap-1.5 pl-1">
|
|
143
130
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
144
|
-
<ResourceRefBadge resourceRef={
|
|
131
|
+
<ResourceRefBadge resourceRef={toBackendRef(b)} onClick={onNavigate} />
|
|
145
132
|
{b.port && <span className="text-theme-text-tertiary">:{b.port}</span>}
|
|
146
133
|
{pct !== null && <span className="text-theme-text-tertiary text-[10px]">{pct}%</span>}
|
|
147
134
|
</div>
|
|
@@ -176,7 +163,7 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
176
163
|
{matchIdx === 0 && backendRefs.length === 1 && (
|
|
177
164
|
<>
|
|
178
165
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
179
|
-
<ResourceRefBadge resourceRef={
|
|
166
|
+
<ResourceRefBadge resourceRef={toBackendRef(backendRefs[0])} onClick={onNavigate} />
|
|
180
167
|
{backendRefs[0].port && <span className="text-theme-text-tertiary">:{backendRefs[0].port}</span>}
|
|
181
168
|
</>
|
|
182
169
|
)}
|
|
@@ -189,7 +176,7 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
189
176
|
return (
|
|
190
177
|
<div key={bi} className="text-xs text-theme-text-secondary flex items-center gap-1.5 pl-1">
|
|
191
178
|
<ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
|
|
192
|
-
<ResourceRefBadge resourceRef={
|
|
179
|
+
<ResourceRefBadge resourceRef={toBackendRef(b)} onClick={onNavigate} />
|
|
193
180
|
{b.port && <span className="text-theme-text-tertiary">:{b.port}</span>}
|
|
194
181
|
{pct !== null && <span className="text-theme-text-tertiary text-[10px]">{pct}%</span>}
|
|
195
182
|
</div>
|
|
@@ -234,7 +221,7 @@ export function HTTPRouteRenderer({ data, onNavigate }: HTTPRouteRendererProps)
|
|
|
234
221
|
)}
|
|
235
222
|
{filter.type === 'RequestMirror' && filter.requestMirror && (
|
|
236
223
|
<span className="text-theme-text-tertiary">
|
|
237
|
-
{filter.requestMirror.backendRef
|
|
224
|
+
{filter.requestMirror.backendRef && <ResourceRefBadge resourceRef={toBackendRef(filter.requestMirror.backendRef)} onClick={onNavigate} />}
|
|
238
225
|
{filter.requestMirror.backendRef?.port ? `:${filter.requestMirror.backendRef.port}` : ''}
|
|
239
226
|
</span>
|
|
240
227
|
)}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Database, CheckCircle2, Shield } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts } from '../../ui/drawer-components'
|
|
3
3
|
import { Badge } from '../../ui/Badge'
|
|
4
|
-
import { formatAge } from '../resource-utils'
|
|
5
4
|
import { formatBytes } from '../../../utils/format'
|
|
6
5
|
import { GitOpsStatusBadge, SyncCountdown } from '../../gitops'
|
|
7
6
|
import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
|
|
7
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
8
8
|
|
|
9
9
|
interface HelmRepositoryRendererProps {
|
|
10
10
|
data: any
|
|
@@ -98,7 +98,7 @@ export function HelmRepositoryRenderer({ data }: HelmRepositoryRendererProps) {
|
|
|
98
98
|
<Property label="Digest" value={artifact.digest} />
|
|
99
99
|
<Property
|
|
100
100
|
label="Last Updated"
|
|
101
|
-
value={
|
|
101
|
+
value={<TimeValue timestamp={artifact.lastUpdateTime} />}
|
|
102
102
|
/>
|
|
103
103
|
{artifact.size && (
|
|
104
104
|
<Property label="Size" value={formatBytes(artifact.size)} />
|
|
@@ -115,7 +115,7 @@ export function HelmRepositoryRenderer({ data }: HelmRepositoryRendererProps) {
|
|
|
115
115
|
{status.lastHandledReconcileAt && (
|
|
116
116
|
<Property
|
|
117
117
|
label="Last Reconciled"
|
|
118
|
-
value={
|
|
118
|
+
value={<TimeValue timestamp={status.lastHandledReconcileAt} />}
|
|
119
119
|
/>
|
|
120
120
|
)}
|
|
121
121
|
</PropertyList>
|
|
@@ -0,0 +1,52 @@
|
|
|
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 { IngressRenderer } from './IngressRenderer'
|
|
6
|
+
import { IngressClassRenderer } from './IngressClassRenderer'
|
|
7
|
+
import { ingressClassParametersResourceRef } from '../../../utils/ingress-class-references'
|
|
8
|
+
|
|
9
|
+
describe('ingressClassParametersResourceRef', () => {
|
|
10
|
+
it('defaults to the core group and cluster scope, and requires a namespace for namespaced parameters', () => {
|
|
11
|
+
expect(ingressClassParametersResourceRef({ kind: 'IngressClassParams', apiGroup: 'elbv2.k8s.aws', name: 'shared' }))
|
|
12
|
+
.toEqual({ kind: 'IngressClassParams', group: 'elbv2.k8s.aws', namespace: '', name: 'shared' })
|
|
13
|
+
expect(ingressClassParametersResourceRef({ kind: 'ConfigMap', scope: 'Namespace', namespace: 'controller', name: 'settings' }))
|
|
14
|
+
.toEqual({ kind: 'ConfigMap', group: '', namespace: 'controller', name: 'settings' })
|
|
15
|
+
expect(ingressClassParametersResourceRef({ kind: 'ConfigMap', scope: 'Namespace', name: 'settings' })).toBeNull()
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('Ingress class navigation', () => {
|
|
20
|
+
it('links the Ingress class and both parameter scopes, but leaves the legacy annotation as text', async () => {
|
|
21
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
|
22
|
+
const container = document.createElement('div')
|
|
23
|
+
const root = createRoot(container)
|
|
24
|
+
const onNavigate = vi.fn()
|
|
25
|
+
const click = async (name: string) => {
|
|
26
|
+
const button = [...container.querySelectorAll('button')].find(button => button.textContent === name)
|
|
27
|
+
expect(button).toBeDefined()
|
|
28
|
+
await act(async () => button!.click())
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
await act(async () => root.render(<IngressRenderer onNavigate={onNavigate} data={{ metadata: { namespace: 'app', annotations: { 'kubernetes.io/ingress.class': 'old-controller' } }, spec: { ingressClassName: 'public' } }} />))
|
|
32
|
+
await click('public')
|
|
33
|
+
expect(onNavigate).toHaveBeenLastCalledWith({ kind: 'ingressclasses', group: 'networking.k8s.io', namespace: '', name: 'public' })
|
|
34
|
+
|
|
35
|
+
await act(async () => root.render(<IngressRenderer onNavigate={onNavigate} data={{ metadata: { annotations: { 'kubernetes.io/ingress.class': 'old-controller' } }, spec: {} }} />))
|
|
36
|
+
expect(container.textContent).toContain('old-controller')
|
|
37
|
+
expect([...container.querySelectorAll('button')].some(button => button.textContent === 'old-controller')).toBe(false)
|
|
38
|
+
|
|
39
|
+
for (const parameters of [
|
|
40
|
+
{ name: 'shared', kind: 'IngressClassParams', apiGroup: 'elbv2.k8s.aws' },
|
|
41
|
+
{ name: 'settings', kind: 'ConfigMap', scope: 'Namespace' as const, namespace: 'controller' },
|
|
42
|
+
]) {
|
|
43
|
+
await act(async () => root.render(<IngressClassRenderer onNavigate={onNavigate} data={{ spec: { controller: 'example.test/ingress', parameters } }} />))
|
|
44
|
+
await click(parameters.name)
|
|
45
|
+
expect(onNavigate).toHaveBeenLastCalledWith(ingressClassParametersResourceRef(parameters))
|
|
46
|
+
}
|
|
47
|
+
} finally {
|
|
48
|
+
await act(async () => root.unmount())
|
|
49
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: false })
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { Globe } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
|
-
import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
3
|
+
import { Section, PropertyList, Property, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
+
import type { ResourceRef } from '../../../types'
|
|
5
|
+
import { ingressClassParametersResourceRef } from '../../../utils/ingress-class-references'
|
|
4
6
|
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
5
7
|
|
|
6
8
|
interface IngressClassRendererProps {
|
|
7
9
|
data: any
|
|
10
|
+
onNavigate?: (ref: ResourceRef) => void
|
|
8
11
|
}
|
|
9
12
|
|
|
10
|
-
export function IngressClassRenderer({ data }: IngressClassRendererProps) {
|
|
13
|
+
export function IngressClassRenderer({ data, onNavigate }: IngressClassRendererProps) {
|
|
11
14
|
const spec = data.spec || {}
|
|
15
|
+
const parametersRef = spec.parameters ? ingressClassParametersResourceRef(spec.parameters) : null
|
|
12
16
|
const annotations = data.metadata?.annotations || {}
|
|
13
17
|
|
|
14
18
|
const isDefault = annotations['ingressclass.kubernetes.io/is-default-class'] === 'true'
|
|
@@ -39,7 +43,10 @@ export function IngressClassRenderer({ data }: IngressClassRendererProps) {
|
|
|
39
43
|
<PropertyList>
|
|
40
44
|
{spec.parameters.apiGroup && <Property label="API Group" value={spec.parameters.apiGroup} />}
|
|
41
45
|
<Property label="Kind" value={spec.parameters.kind} />
|
|
42
|
-
<Property label="Name" value={
|
|
46
|
+
<Property label="Name" value={parametersRef
|
|
47
|
+
? <ResourceLink {...parametersRef} onNavigate={onNavigate} />
|
|
48
|
+
: spec.parameters.name
|
|
49
|
+
} />
|
|
43
50
|
{spec.parameters.namespace && <Property label="Namespace" value={spec.parameters.namespace} />}
|
|
44
51
|
{spec.parameters.scope && <Property label="Scope" value={spec.parameters.scope} />}
|
|
45
52
|
</PropertyList>
|