@skyhook-io/k8s-ui 1.17.0 → 1.18.0
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 +6 -6
- 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/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 +7 -3
- package/src/components/gitops/GitOpsHealthSourceNotice.test.tsx +38 -0
- package/src/components/gitops/GitOpsHealthSourceNotice.tsx +13 -3
- package/src/components/gitops/insights/ArgoResourceDiff.test.tsx +142 -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 +40 -81
- package/src/components/gitops/insights/insights-helpers.test.ts +18 -0
- package/src/components/gitops/insights/insights-helpers.ts +7 -0
- package/src/components/gitops/tree/GitOpsTreeGraph.test.ts +91 -2
- package/src/components/gitops/tree/GitOpsTreeGraph.tsx +76 -76
- 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 +23 -0
- package/src/components/gitops/tree/tree-helpers.ts +12 -2
- package/src/components/issues/ConditionalCleanup.test.tsx +82 -0
- package/src/components/issues/IssuesView.tsx +44 -6
- package/src/components/issues/ResourceIssuesSection.test.tsx +28 -0
- package/src/components/issues/ResourceIssuesSection.tsx +8 -2
- package/src/components/issues/diagnostic.ts +2 -0
- package/src/components/issues/index.ts +3 -1
- package/src/components/issues/issue-timing.ts +12 -1
- package/src/components/issues/issues.test.ts +75 -1
- package/src/components/issues/severity.ts +30 -2
- package/src/components/issues/types.ts +55 -4
- package/src/components/logs/LogCore.theme.test.tsx +206 -3
- package/src/components/logs/LogCore.tsx +180 -53
- package/src/components/logs/LogToolbarSelects.tsx +3 -1
- package/src/components/logs/LogsViewer.test.tsx +369 -0
- package/src/components/logs/LogsViewer.tsx +91 -13
- package/src/components/logs/StructuredLogLine.test.tsx +76 -0
- package/src/components/logs/StructuredLogLine.tsx +23 -31
- package/src/components/logs/WorkloadLogsViewer.test.tsx +65 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +46 -11
- package/src/components/logs/useLogBuffer.reading.test.tsx +149 -0
- package/src/components/logs/useLogBuffer.test.ts +8 -0
- package/src/components/logs/useLogBuffer.ts +48 -18
- package/src/components/logs/useLogSearch.ts +3 -1
- package/src/components/logs/useLogStream.ts +17 -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/ColumnPickerList.test.tsx +46 -0
- package/src/components/resources/ColumnPickerList.tsx +174 -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 +296 -0
- package/src/components/resources/ResourcesView.per-container.test.ts +56 -1
- package/src/components/resources/ResourcesView.tsx +392 -181
- package/src/components/resources/column-order.test.ts +104 -0
- package/src/components/resources/column-order.ts +64 -0
- package/src/components/resources/curated-column-ownership.test.ts +3 -3
- package/src/components/resources/renderers/ArgoApplicationRenderer.test.tsx +15 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +4 -1
- package/src/components/resources/renderers/AutoscalingReferences.test.tsx +286 -0
- package/src/components/resources/renderers/CAPIClusterRenderer.tsx +3 -2
- package/src/components/resources/renderers/CAPIExactReferenceNavigation.test.tsx +27 -0
- 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 +5 -7
- 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 +7 -15
- 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/ContourExactReferenceNavigation.test.tsx +34 -0
- package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +11 -10
- package/src/components/resources/renderers/CronJobRenderer.test.tsx +31 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +28 -20
- package/src/components/resources/renderers/EndpointSliceRenderer.test.tsx +39 -1
- package/src/components/resources/renderers/EndpointSliceRenderer.tsx +14 -13
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +22 -9
- package/src/components/resources/renderers/HPARenderer.tsx +56 -16
- package/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx +33 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +30 -3
- 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/KedaScaledObjectRenderer.tsx +13 -11
- 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/ManagedResourceRenderer.tsx +2 -2
- package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +3 -2
- package/src/components/resources/renderers/PodRenderer.sidecar-terminal.test.tsx +200 -0
- package/src/components/resources/renderers/PodRenderer.test.tsx +32 -0
- package/src/components/resources/renderers/PodRenderer.tsx +81 -40
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +1 -1
- package/src/components/resources/renderers/VPARenderer.tsx +17 -11
- 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 -20
- 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-knative.ts +18 -0
- package/src/components/resources/resource-utils.ts +96 -7
- package/src/components/resources/rules-column-width.test.ts +1 -1
- package/src/components/resources/summarize-scheduler-message.test.ts +31 -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.test.tsx +7 -0
- package/src/components/shared/DrainPlanDialog.tsx +6 -2
- package/src/components/shared/EditableYamlView.test.tsx +7 -0
- package/src/components/shared/EditableYamlView.tsx +1 -1
- package/src/components/shared/ResourceActionsBar.tsx +14 -7
- package/src/components/shared/ResourceRendererDispatch.tsx +15 -10
- package/src/components/shared/index.ts +2 -0
- package/src/components/timeline/DiffViewer.test.tsx +69 -0
- package/src/components/timeline/DiffViewer.tsx +113 -4
- package/src/components/timeline/TimelineSwimlanes.tsx +1 -1
- package/src/components/timeline/shared.test.tsx +48 -0
- package/src/components/timeline/shared.tsx +14 -19
- 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 +3 -71
- 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/Toast.tsx +15 -4
- package/src/components/ui/crd-definition-navigation.test.tsx +160 -0
- package/src/components/ui/drawer-components.test.tsx +110 -1
- package/src/components/ui/drawer-components.tsx +62 -9
- package/src/components/ui/index.ts +4 -2
- 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 +43 -0
- package/src/components/workload/WorkloadView.relationships.test.tsx +37 -0
- package/src/components/workload/WorkloadView.spec.test.tsx +149 -0
- package/src/components/workload/WorkloadView.tsx +193 -71
- 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 +11 -2
- package/src/types/core.ts +53 -2
- package/src/types/gitops-flux-status.test.ts +32 -1
- package/src/types/gitops-insights.ts +20 -2
- package/src/types/gitops-tree.ts +12 -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 +9 -0
- package/src/utils/format.ts +4 -0
- package/src/utils/gitops-owner.ts +3 -2
- package/src/utils/grant.test.ts +19 -0
- package/src/utils/grant.ts +35 -0
- package/src/utils/index.ts +4 -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 +57 -0
- package/src/utils/navigation.ts +86 -8
- package/src/utils/object-reference-navigation.test.ts +43 -0
- package/src/utils/resource-hierarchy.test.ts +37 -0
- package/src/utils/resource-hierarchy.ts +15 -7
- package/src/utils/resource-icons.ts +2 -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,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 {
|
|
6
|
+
import { objectReferenceToResourceRef, apiVersionToGroup } from '../../../utils/navigation'
|
|
7
|
+
import type { ResourceRef } from '../../../types'
|
|
8
|
+
import { TimeValue } from '../../ui/ScheduleValue'
|
|
7
9
|
import { HPADiagnosisSummary } from '../HPADiagnosisSummary'
|
|
8
10
|
import type { HPADiagnosis } from '../../../types'
|
|
9
|
-
import { TimeValue } from '../../ui/ScheduleValue'
|
|
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,15 +55,9 @@ 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} />
|
|
@@ -70,11 +70,11 @@ export function HPARenderer({ data, onNavigate, hpaDiagnosis, extraSections }: H
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { renderToString } from 'react-dom/server'
|
|
3
|
+
import { IngressRenderer } from './IngressRenderer'
|
|
4
|
+
|
|
5
|
+
describe('IngressRenderer default backend', () => {
|
|
6
|
+
it('shows the default backend and no "not routed" warning', () => {
|
|
7
|
+
const html = renderToString(
|
|
8
|
+
<IngressRenderer data={{
|
|
9
|
+
metadata: { name: 'catch-all', namespace: 'team' },
|
|
10
|
+
spec: { ingressClassName: 'nginx', defaultBackend: { service: { name: 'fallback', port: { number: 80 } } } },
|
|
11
|
+
}} />,
|
|
12
|
+
)
|
|
13
|
+
expect(html).toContain('Default Backend')
|
|
14
|
+
expect(html.replace(/<!-- -->/g, '')).toContain('fallback:80')
|
|
15
|
+
expect(html).not.toContain('Traffic will not be routed')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('still warns when an Ingress routes nowhere', () => {
|
|
19
|
+
const html = renderToString(<IngressRenderer data={{ metadata: { name: 'empty', namespace: 'team' }, spec: { ingressClassName: 'nginx' } }} />)
|
|
20
|
+
expect(html).toContain('Traffic will not be routed')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('shows a resource default backend', () => {
|
|
24
|
+
const html = renderToString(
|
|
25
|
+
<IngressRenderer data={{
|
|
26
|
+
metadata: { name: 'static', namespace: 'team' },
|
|
27
|
+
spec: { ingressClassName: 'nginx', defaultBackend: { resource: { apiGroup: 'k8s.example.com', kind: 'StorageBucket', name: 'static-assets' } } },
|
|
28
|
+
}} />,
|
|
29
|
+
)
|
|
30
|
+
expect(html).toContain('Default Backend')
|
|
31
|
+
expect(html).toContain('StorageBucket/static-assets')
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Globe, Shield, Clock } from 'lucide-react'
|
|
2
2
|
import type { ResourceRef } from '../../../types'
|
|
3
3
|
import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
+
import { kindToPluralWithGroup } from '../../../utils/navigation'
|
|
4
5
|
|
|
5
6
|
interface IngressRendererProps {
|
|
6
7
|
data: any
|
|
@@ -16,7 +17,10 @@ export function IngressRenderer({ data, onNavigate }: IngressRendererProps) {
|
|
|
16
17
|
// Check for issues
|
|
17
18
|
const hasNoAddress = lbIngress.length === 0
|
|
18
19
|
const hasNoClass = !spec.ingressClassName && !data.metadata?.annotations?.['kubernetes.io/ingress.class']
|
|
19
|
-
const
|
|
20
|
+
const defaultService = spec.defaultBackend?.service
|
|
21
|
+
const defaultResource = spec.defaultBackend?.resource
|
|
22
|
+
// An Ingress with only a default backend still routes everything to it.
|
|
23
|
+
const hasNoRules = rules.length === 0 && !spec.defaultBackend
|
|
20
24
|
|
|
21
25
|
return (
|
|
22
26
|
<>
|
|
@@ -50,11 +54,34 @@ export function IngressRenderer({ data, onNavigate }: IngressRendererProps) {
|
|
|
50
54
|
{lbIngress.length > 0 && (
|
|
51
55
|
<Property label="Address" value={lbIngress[0].ip || lbIngress[0].hostname} />
|
|
52
56
|
)}
|
|
57
|
+
{defaultService?.name && (
|
|
58
|
+
<Property label="Default Backend" value={
|
|
59
|
+
<ResourceLink
|
|
60
|
+
name={defaultService.name}
|
|
61
|
+
kind="services"
|
|
62
|
+
namespace={data.metadata?.namespace || ''}
|
|
63
|
+
label={<span className="text-blue-400">{defaultService.name}:{defaultService.port?.number || defaultService.port?.name}</span>}
|
|
64
|
+
onNavigate={onNavigate}
|
|
65
|
+
/>
|
|
66
|
+
} />
|
|
67
|
+
)}
|
|
68
|
+
{defaultResource?.name && (
|
|
69
|
+
<Property label="Default Backend" value={
|
|
70
|
+
<ResourceLink
|
|
71
|
+
name={defaultResource.name}
|
|
72
|
+
kind={kindToPluralWithGroup(defaultResource.kind, defaultResource.apiGroup || '')}
|
|
73
|
+
group={defaultResource.apiGroup || ''}
|
|
74
|
+
namespace={data.metadata?.namespace || ''}
|
|
75
|
+
label={<span className="text-blue-400">{`${defaultResource.kind}/${defaultResource.name}`}</span>}
|
|
76
|
+
onNavigate={onNavigate}
|
|
77
|
+
/>
|
|
78
|
+
} />
|
|
79
|
+
)}
|
|
53
80
|
<Property label="TLS" value={tls.length > 0 ? `${tls.length} certificate(s)` : 'None'} />
|
|
54
81
|
</PropertyList>
|
|
55
82
|
</Section>
|
|
56
83
|
|
|
57
|
-
<Section title="Rules" defaultExpanded>
|
|
84
|
+
{rules.length > 0 && <Section title="Rules" defaultExpanded>
|
|
58
85
|
<div className="space-y-3">
|
|
59
86
|
{rules.map((rule: any, i: number) => (
|
|
60
87
|
<div key={i} className="card-inner-lg">
|
|
@@ -87,7 +114,7 @@ export function IngressRenderer({ data, onNavigate }: IngressRendererProps) {
|
|
|
87
114
|
</div>
|
|
88
115
|
))}
|
|
89
116
|
</div>
|
|
90
|
-
</Section>
|
|
117
|
+
</Section>}
|
|
91
118
|
|
|
92
119
|
{tls.length > 0 && (
|
|
93
120
|
<Section title="TLS" icon={Shield}>
|
|
@@ -2,6 +2,7 @@ import { Network } from 'lucide-react'
|
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, ResourceLink, KeyValueBadgeList } from '../../ui/drawer-components'
|
|
3
3
|
import {
|
|
4
4
|
getDestinationRuleHost,
|
|
5
|
+
resolveIstioServiceHost,
|
|
5
6
|
getDestinationRuleSubsets,
|
|
6
7
|
getDestinationRuleTrafficPolicy,
|
|
7
8
|
getDestinationRuleLoadBalancer,
|
|
@@ -9,20 +10,18 @@ import {
|
|
|
9
10
|
|
|
10
11
|
interface IstioDestinationRuleRendererProps {
|
|
11
12
|
data: any
|
|
13
|
+
serviceDomain?: string
|
|
12
14
|
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export function IstioDestinationRuleRenderer({ data, onNavigate }: IstioDestinationRuleRendererProps) {
|
|
17
|
+
export function IstioDestinationRuleRenderer({ data, onNavigate, serviceDomain }: IstioDestinationRuleRendererProps) {
|
|
16
18
|
const host = getDestinationRuleHost(data)
|
|
17
19
|
const subsets = getDestinationRuleSubsets(data)
|
|
18
20
|
const trafficPolicy = getDestinationRuleTrafficPolicy(data)
|
|
19
21
|
const loadBalancer = getDestinationRuleLoadBalancer(data)
|
|
20
22
|
const ns = data.metadata?.namespace || ''
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
const hostParts = host.split('.')
|
|
24
|
-
const svcName = hostParts[0]
|
|
25
|
-
const svcNs = hostParts.length >= 2 ? hostParts[1] : ns
|
|
24
|
+
const service = resolveIstioServiceHost(host, ns, serviceDomain)
|
|
26
25
|
|
|
27
26
|
return (
|
|
28
27
|
<>
|
|
@@ -30,15 +29,16 @@ export function IstioDestinationRuleRenderer({ data, onNavigate }: IstioDestinat
|
|
|
30
29
|
<Section title="Destination" icon={Network} defaultExpanded>
|
|
31
30
|
<PropertyList>
|
|
32
31
|
<Property label="Host" value={
|
|
33
|
-
|
|
32
|
+
service ? (
|
|
34
33
|
<ResourceLink
|
|
35
|
-
name={
|
|
34
|
+
name={service.name}
|
|
36
35
|
kind="services"
|
|
37
|
-
|
|
36
|
+
group=""
|
|
37
|
+
namespace={service.namespace}
|
|
38
38
|
label={host}
|
|
39
39
|
onNavigate={onNavigate}
|
|
40
40
|
/>
|
|
41
|
-
) :
|
|
41
|
+
) : host
|
|
42
42
|
} />
|
|
43
43
|
<Property label="Load Balancer" value={loadBalancer} />
|
|
44
44
|
</PropertyList>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act, type ReactNode } from 'react'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
5
|
+
import { IstioDestinationRuleRenderer } from './IstioDestinationRuleRenderer'
|
|
6
|
+
|
|
7
|
+
async function rendered(element: ReactNode, check: (container: HTMLDivElement) => Promise<void> | void) {
|
|
8
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
|
9
|
+
const container = document.createElement('div')
|
|
10
|
+
const root = createRoot(container)
|
|
11
|
+
try { await act(async () => root.render(element)); await check(container) }
|
|
12
|
+
finally { await act(async () => root.unmount()); Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: false }) }
|
|
13
|
+
}
|
|
14
|
+
async function click(container: HTMLDivElement, text: string) {
|
|
15
|
+
const button = [...container.querySelectorAll('button')].find(button => button.textContent === text)
|
|
16
|
+
expect(button, `missing link ${text}`).toBeDefined()
|
|
17
|
+
await act(async () => button!.click())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('exact reference navigation', () => {
|
|
21
|
+
it.each([
|
|
22
|
+
{host:'api.prod.svc.mesh.internal',domain:'mesh.internal',linked:true},
|
|
23
|
+
{host:'api.prod.svc.cluster.local',domain:undefined,linked:false},
|
|
24
|
+
{host:'api.prod.svc.external.test',domain:'mesh.internal',linked:false},
|
|
25
|
+
{host:'api.prod',domain:'mesh.internal',linked:false},
|
|
26
|
+
])('links only observed Istio service domains: $host',async ({host,domain,linked})=>{
|
|
27
|
+
const navigate=vi.fn()
|
|
28
|
+
await rendered(<IstioDestinationRuleRenderer data={{metadata:{namespace:'app'},spec:{host}}} serviceDomain={domain} onNavigate={navigate}/>,async container=>{
|
|
29
|
+
const link=[...container.querySelectorAll('button')].find(button=>button.textContent===host)
|
|
30
|
+
expect(Boolean(link)).toBe(linked)
|
|
31
|
+
if(link) {await click(container,host);expect(navigate).toHaveBeenCalledWith({kind:'services',group:'',namespace:'prod',name:'api'})}
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getVirtualServiceHostsList,
|
|
8
8
|
getVirtualServiceGatewaysList,
|
|
9
9
|
getVirtualServiceHttpRoutes,
|
|
10
|
+
resolveIstioServiceHost,
|
|
10
11
|
getVirtualServiceTcpRoutes,
|
|
11
12
|
getVirtualServiceTlsRoutes,
|
|
12
13
|
getVirtualServiceRouteCount,
|
|
@@ -14,10 +15,11 @@ import {
|
|
|
14
15
|
|
|
15
16
|
interface IstioVirtualServiceRendererProps {
|
|
16
17
|
data: any
|
|
18
|
+
serviceDomain?: string
|
|
17
19
|
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export function IstioVirtualServiceRenderer({ data, onNavigate }: IstioVirtualServiceRendererProps) {
|
|
22
|
+
export function IstioVirtualServiceRenderer({ data, onNavigate, serviceDomain }: IstioVirtualServiceRendererProps) {
|
|
21
23
|
const status = getVirtualServiceStatus(data)
|
|
22
24
|
const hosts = getVirtualServiceHostsList(data)
|
|
23
25
|
const gateways = getVirtualServiceGatewaysList(data)
|
|
@@ -148,6 +150,7 @@ export function IstioVirtualServiceRenderer({ data, onNavigate }: IstioVirtualSe
|
|
|
148
150
|
{route.route.map((dest: any, di: number) => {
|
|
149
151
|
const weight = dest.weight ?? (route.route.length === 1 ? 100 : undefined)
|
|
150
152
|
const destHost = dest.destination?.host || ''
|
|
153
|
+
const service = resolveIstioServiceHost(destHost, ns, serviceDomain)
|
|
151
154
|
const destPort = dest.destination?.port?.number
|
|
152
155
|
const destSubset = dest.destination?.subset
|
|
153
156
|
|
|
@@ -164,13 +167,7 @@ export function IstioVirtualServiceRenderer({ data, onNavigate }: IstioVirtualSe
|
|
|
164
167
|
<span className="text-theme-text-secondary font-medium">{weight}%</span>
|
|
165
168
|
</div>
|
|
166
169
|
)}
|
|
167
|
-
<ResourceLink
|
|
168
|
-
name={destHost.split('.')[0]}
|
|
169
|
-
kind="services"
|
|
170
|
-
namespace={destHost.includes('.') ? destHost.split('.')[1] : ns}
|
|
171
|
-
label={destHost}
|
|
172
|
-
onNavigate={onNavigate}
|
|
173
|
-
/>
|
|
170
|
+
{service ? <ResourceLink name={service.name} kind="services" group="" namespace={service.namespace} label={destHost} onNavigate={onNavigate} /> : <span className="text-theme-text-primary break-all">{destHost}</span>}
|
|
174
171
|
{destPort && (
|
|
175
172
|
<span className="text-theme-text-tertiary">:{destPort}</span>
|
|
176
173
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Cpu, Settings } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink, useOperationalIssuesShown} from '../../ui/drawer-components'
|
|
3
|
-
import {
|
|
3
|
+
import { objectReferenceToResourceRef } from '../../../utils/navigation'
|
|
4
|
+
import type { ResourceRef } from '../../../types'
|
|
4
5
|
import { TimeValue } from '../../ui/ScheduleValue'
|
|
5
6
|
import {
|
|
6
7
|
getScaledObjectStatus,
|
|
@@ -24,7 +25,7 @@ function summarizeScalingPolicies(policies: any[] | undefined, periodSeconds?: n
|
|
|
24
25
|
|
|
25
26
|
interface KedaScaledObjectRendererProps {
|
|
26
27
|
data: any
|
|
27
|
-
onNavigate?: (ref:
|
|
28
|
+
onNavigate?: (ref: ResourceRef) => void
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function KedaScaledObjectRenderer({ data, onNavigate }: KedaScaledObjectRendererProps) {
|
|
@@ -75,18 +76,19 @@ export function KedaScaledObjectRenderer({ data, onNavigate }: KedaScaledObjectR
|
|
|
75
76
|
<Property label="Target" value={(() => {
|
|
76
77
|
const target = data.spec?.scaleTargetRef
|
|
77
78
|
if (target?.name) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
79
|
+
// KEDA documents these defaults for omitted fields; an explicit
|
|
80
|
+
// target API group still belongs to the declared custom resource.
|
|
81
|
+
const ref = objectReferenceToResourceRef({
|
|
82
|
+
...target,
|
|
83
|
+
apiVersion: target.apiVersion || 'apps/v1',
|
|
84
|
+
kind: target.kind || 'Deployment',
|
|
85
|
+
namespace: data.metadata?.namespace,
|
|
86
|
+
})
|
|
87
|
+
return ref ? <ResourceLink {...ref} label={getScaledObjectTarget(data)} onNavigate={onNavigate} /> : getScaledObjectTarget(data)
|
|
87
88
|
}
|
|
88
89
|
return getScaledObjectTarget(data)
|
|
89
90
|
})()} />
|
|
91
|
+
<Property label="Target API" value={data.spec?.scaleTargetRef?.apiVersion || 'apps/v1'} />
|
|
90
92
|
<Property label="Replicas" value={getScaledObjectReplicas(data)} />
|
|
91
93
|
<Property label="Polling Interval" value={`${getScaledObjectPollingInterval(data)}s`} />
|
|
92
94
|
<Property label="Cooldown Period" value={`${getScaledObjectCooldownPeriod(data)}s`} />
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { KnativeReferenceLink } from './KnativeReferenceLink'
|
|
1
2
|
import { Radio, Filter, FileType, Inbox, ArrowRightLeft } from 'lucide-react'
|
|
2
3
|
import { clsx } from 'clsx'
|
|
3
4
|
import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
-
import { kindToPlural } from '../../../utils/navigation'
|
|
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
|
// ============================================================================
|
|
@@ -47,10 +47,9 @@ export function BrokerRenderer({ data, onNavigate }: RendererProps) {
|
|
|
47
47
|
<PropertyList>
|
|
48
48
|
{delivery.deadLetterSink?.ref && (
|
|
49
49
|
<Property label="Dead Letter" value={
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
namespace={delivery.deadLetterSink.ref.namespace || ns}
|
|
50
|
+
<KnativeReferenceLink
|
|
51
|
+
reference={delivery.deadLetterSink.ref}
|
|
52
|
+
namespace={ns}
|
|
54
53
|
onNavigate={onNavigate}
|
|
55
54
|
/>
|
|
56
55
|
} />
|
|
@@ -96,19 +95,17 @@ export function TriggerRenderer({ data, onNavigate }: RendererProps) {
|
|
|
96
95
|
</span>
|
|
97
96
|
} />
|
|
98
97
|
<Property label="Broker" value={
|
|
99
|
-
|
|
100
|
-
name
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
onNavigate={onNavigate}
|
|
104
|
-
/>
|
|
98
|
+
spec.brokerRef
|
|
99
|
+
? (spec.brokerRef.name && spec.brokerRef.namespace
|
|
100
|
+
? <ResourceLink name={spec.brokerRef.name} kind="brokers" group="eventing.knative.dev" namespace={spec.brokerRef.namespace} onNavigate={onNavigate} />
|
|
101
|
+
: <span>{spec.brokerRef.name || '-'}</span>)
|
|
102
|
+
: <ResourceLink name={brokerName} kind="brokers" group="eventing.knative.dev" namespace={ns} onNavigate={onNavigate} />
|
|
105
103
|
} />
|
|
106
104
|
<Property label="Subscriber" value={
|
|
107
105
|
subscriberRef ? (
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
namespace={subscriberRef.namespace || ns}
|
|
106
|
+
<KnativeReferenceLink
|
|
107
|
+
reference={subscriberRef}
|
|
108
|
+
namespace={ns}
|
|
112
109
|
onNavigate={onNavigate}
|
|
113
110
|
/>
|
|
114
111
|
) : subscriberUri ? (
|
|
@@ -247,9 +244,8 @@ export function SubscriptionRenderer({ data, onNavigate }: RendererProps) {
|
|
|
247
244
|
} />
|
|
248
245
|
{channelRef && (
|
|
249
246
|
<Property label="Channel" value={
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
kind={kindToPlural(channelRef.kind || 'Channel')}
|
|
247
|
+
<KnativeReferenceLink
|
|
248
|
+
reference={channelRef}
|
|
253
249
|
namespace={ns}
|
|
254
250
|
onNavigate={onNavigate}
|
|
255
251
|
/>
|
|
@@ -257,10 +253,9 @@ export function SubscriptionRenderer({ data, onNavigate }: RendererProps) {
|
|
|
257
253
|
)}
|
|
258
254
|
<Property label="Subscriber" value={
|
|
259
255
|
subscriberRef ? (
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
namespace={subscriberRef.namespace || ns}
|
|
256
|
+
<KnativeReferenceLink
|
|
257
|
+
reference={subscriberRef}
|
|
258
|
+
namespace={ns}
|
|
264
259
|
onNavigate={onNavigate}
|
|
265
260
|
/>
|
|
266
261
|
) : subscriberUri ? (
|
|
@@ -269,10 +264,9 @@ export function SubscriptionRenderer({ data, onNavigate }: RendererProps) {
|
|
|
269
264
|
} />
|
|
270
265
|
<Property label="Reply" value={
|
|
271
266
|
replyRef ? (
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
namespace={replyRef.namespace || ns}
|
|
267
|
+
<KnativeReferenceLink
|
|
268
|
+
reference={replyRef}
|
|
269
|
+
namespace={ns}
|
|
276
270
|
onNavigate={onNavigate}
|
|
277
271
|
/>
|
|
278
272
|
) : replyUri ? (
|
|
@@ -287,10 +281,9 @@ export function SubscriptionRenderer({ data, onNavigate }: RendererProps) {
|
|
|
287
281
|
<PropertyList>
|
|
288
282
|
{delivery.deadLetterSink.ref && (
|
|
289
283
|
<Property label="Sink" value={
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
namespace={delivery.deadLetterSink.ref.namespace || ns}
|
|
284
|
+
<KnativeReferenceLink
|
|
285
|
+
reference={delivery.deadLetterSink.ref}
|
|
286
|
+
namespace={ns}
|
|
294
287
|
onNavigate={onNavigate}
|
|
295
288
|
/>
|
|
296
289
|
} />
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { act, type ReactNode } from 'react'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
5
|
+
import { KnativeReferenceLink } from './KnativeReferenceLink'
|
|
6
|
+
import { TriggerRenderer } from './KnativeEventingRenderer'
|
|
7
|
+
|
|
8
|
+
async function rendered(element: ReactNode, check: (container: HTMLDivElement) => Promise<void> | void) {
|
|
9
|
+
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
|
10
|
+
const container = document.createElement('div')
|
|
11
|
+
const root = createRoot(container)
|
|
12
|
+
try { await act(async () => root.render(element)); await check(container) }
|
|
13
|
+
finally { await act(async () => root.unmount()); Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: false }) }
|
|
14
|
+
}
|
|
15
|
+
async function click(container: HTMLDivElement, text: string) {
|
|
16
|
+
const button = [...container.querySelectorAll('button')].find(button => button.textContent === text)
|
|
17
|
+
expect(button, `missing link ${text}`).toBeDefined()
|
|
18
|
+
await act(async () => button!.click())
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('exact reference navigation', () => {
|
|
22
|
+
it('follows the Trigger broker reconciler namespace/name contract',async()=>{
|
|
23
|
+
const navigate=vi.fn()
|
|
24
|
+
await rendered(<TriggerRenderer data={{metadata:{namespace:'app'},spec:{brokerRef:{namespace:'events',name:'actual',kind:'Service',apiVersion:'v1'}}}} onNavigate={navigate}/>,async container=>{
|
|
25
|
+
await click(container,'actual');expect(navigate).toHaveBeenCalledWith({kind:'brokers',group:'eventing.knative.dev',namespace:'events',name:'actual'})
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it.each([
|
|
30
|
+
{apiVersion:'messaging.knative.dev/v1',kind:'InMemoryChannel',name:'events'},
|
|
31
|
+
{group:'messaging.knative.dev',kind:'InMemoryChannel',name:'events'},
|
|
32
|
+
])('uses the actual Knative target group and kind: %j', async reference => {
|
|
33
|
+
const navigate=vi.fn()
|
|
34
|
+
await rendered(<KnativeReferenceLink reference={reference} namespace="app" onNavigate={navigate}/>,async container=>{
|
|
35
|
+
await click(container,'events')
|
|
36
|
+
expect(navigate).toHaveBeenCalledWith({kind:'inmemorychannels',group:'messaging.knative.dev',namespace:'app',name:'events'})
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
it.each([
|
|
40
|
+
{kind:'Service',name:'events'},
|
|
41
|
+
{apiVersion:'v1',group:'serving.knative.dev',kind:'Service',name:'events'},
|
|
42
|
+
])('leaves incomplete or conflicting Knative identity as text: %j', async reference=>{
|
|
43
|
+
await rendered(<KnativeReferenceLink reference={reference} namespace="app" onNavigate={vi.fn()}/>,container=>{
|
|
44
|
+
expect(container.textContent).toContain('events');expect(container.querySelector('button')).toBeNull()
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { KnativeReferenceLink } from './KnativeReferenceLink'
|
|
1
2
|
import { ListOrdered, GitFork } 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 RefDisplay({ ref: destRef, ns, onNavigate }: { ref: any; ns: string; onNavigate?: RendererProps['onNavigate'] }) {
|
|
@@ -14,10 +14,9 @@ function RefDisplay({ ref: destRef, ns, onNavigate }: { ref: any; ns: string; on
|
|
|
14
14
|
|
|
15
15
|
if (destRef.ref) {
|
|
16
16
|
return (
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
namespace={destRef.ref.namespace || ns}
|
|
17
|
+
<KnativeReferenceLink
|
|
18
|
+
reference={destRef.ref}
|
|
19
|
+
namespace={ns}
|
|
21
20
|
onNavigate={onNavigate}
|
|
22
21
|
/>
|
|
23
22
|
)
|
|
@@ -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
|
+
}
|