@skyhook-io/radar-app 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 +1 -1
- package/package.json +3 -3
- package/src/App.tsx +138 -93
- package/src/RadarApp.tsx +27 -17
- package/src/api/actions.errorCode.test.ts +40 -0
- package/src/api/actions.test.ts +48 -0
- package/src/api/actions.ts +79 -0
- package/src/api/apiResources.navigation.test.tsx +232 -0
- package/src/api/apiResources.ts +46 -3
- package/src/api/client.goneResource.test.ts +49 -0
- package/src/api/client.helmSource.test.ts +39 -0
- package/src/api/client.namespaceSwitch.hook.test.tsx +54 -0
- package/src/api/client.namespaceSwitch.test.ts +107 -0
- package/src/api/client.ts +381 -47
- package/src/api/cnpg-ha.live.test.ts +140 -0
- package/src/api/cnpg-ha.test.ts +78 -0
- package/src/api/cnpg-ha.ts +19 -0
- package/src/api/cnpg-history.ts +123 -0
- package/src/api/cnpg-inspect.ts +102 -0
- package/src/api/cnpg-protection.ts +112 -0
- package/src/api/cnpg-recovery.test.tsx +60 -0
- package/src/api/cnpg-recovery.ts +262 -0
- package/src/api/cnpg-sessions.ts +175 -0
- package/src/api/cnpg-storage.ts +138 -0
- package/src/api/cnpg.legacy-reads.test.ts +43 -0
- package/src/api/cnpg.pooler-runtime.test.ts +15 -0
- package/src/api/cnpg.ts +428 -10
- package/src/api/diagnose.test.ts +40 -0
- package/src/api/diagnose.ts +12 -1
- package/src/api/radarFeatures.render.test.tsx +65 -3
- package/src/api/radarFeatures.ts +6 -0
- package/src/api/reviewedActionMutation.test.tsx +164 -0
- package/src/api/reviewedActionMutation.ts +66 -0
- package/src/api/timelineSource.test.ts +9 -0
- package/src/api/timelineSource.ts +8 -0
- package/src/api/traffic.sources.test.ts +55 -0
- package/src/api/traffic.ts +173 -14
- package/src/components/applications/ApplicationsView.tsx +3 -0
- package/src/components/audit/AuditView.tsx +9 -0
- package/src/components/capacity/CapacityActivity.tsx +3 -31
- package/src/components/capacity/CapacityDemand.tsx +4 -36
- package/src/components/capacity/CapacityOverview.tsx +217 -56
- package/src/components/capacity/CapacityPoolDetail.tsx +24 -62
- package/src/components/capacity/CapacityView.test.tsx +354 -1
- package/src/components/capacity/ClusterSchedulingCard.tsx +2 -10
- package/src/components/capacity/certaintyGlyph.test.tsx +2 -9
- package/src/components/capacity/routes.ts +42 -0
- package/src/components/capacity/shared.tsx +23 -150
- package/src/components/capacity/workspaceRoute.ts +18 -0
- package/src/components/cnpg/CNPGActionHost.compatibility.test.tsx +30 -0
- package/src/components/cnpg/CNPGArchivingRepair.tsx +191 -0
- package/src/components/cnpg/CNPGBlockingSessions.test.tsx +50 -0
- package/src/components/cnpg/CNPGBlockingSessions.tsx +333 -0
- package/src/components/cnpg/CNPGClusterActivity.test.tsx +11 -0
- package/src/components/cnpg/CNPGClusterActivity.tsx +18 -9
- package/src/components/cnpg/CNPGClusterLogs.compatibility.test.tsx +130 -0
- package/src/components/cnpg/CNPGClusterLogs.empty.test.tsx +29 -0
- package/src/components/cnpg/CNPGClusterLogs.test.ts +9 -0
- package/src/components/cnpg/CNPGClusterLogs.tsx +81 -24
- package/src/components/cnpg/CNPGClusterTabs.test.tsx +152 -0
- package/src/components/cnpg/CNPGClusterTabs.tsx +187 -0
- package/src/components/cnpg/CNPGClustersKindView.tsx +46 -0
- package/src/components/cnpg/CNPGConfiguration.test.tsx +223 -0
- package/src/components/cnpg/CNPGConfiguration.tsx +190 -0
- package/src/components/cnpg/CNPGCreateClusterDialog.test.tsx +113 -0
- package/src/components/cnpg/CNPGCreateClusterDialog.tsx +74 -0
- package/src/components/cnpg/CNPGDeclarations.test.ts +107 -0
- package/src/components/cnpg/CNPGDeclarations.tsx +236 -78
- package/src/components/cnpg/CNPGDetailPage.test.tsx +62 -0
- package/src/components/cnpg/CNPGDetailPage.tsx +159 -98
- package/src/components/cnpg/CNPGDrawerTrail.tsx +2 -2
- package/src/components/cnpg/CNPGInvestigationAction.test.tsx +30 -0
- package/src/components/cnpg/CNPGOperator.test.tsx +34 -0
- package/src/components/cnpg/CNPGOperator.tsx +78 -30
- package/src/components/cnpg/CNPGOperatorBanner.tsx +39 -0
- package/src/components/cnpg/CNPGOperatorConditions.test.tsx +45 -0
- package/src/components/cnpg/CNPGOperatorConditions.tsx +16 -0
- package/src/components/cnpg/CNPGOperatorConfig.test.tsx +27 -0
- package/src/components/cnpg/CNPGOperatorDiagnosis.tsx +247 -0
- package/src/components/cnpg/CNPGOverview.test.tsx +171 -0
- package/src/components/cnpg/CNPGOverview.tsx +238 -63
- package/src/components/cnpg/CNPGParametersInEffect.test.tsx +51 -0
- package/src/components/cnpg/CNPGParametersInEffect.tsx +120 -0
- package/src/components/cnpg/CNPGPerformance.prerequisite.test.tsx +48 -0
- package/src/components/cnpg/CNPGPerformance.test.tsx +73 -0
- package/src/components/cnpg/CNPGPerformance.tsx +477 -0
- package/src/components/cnpg/CNPGPooling.test.tsx +89 -0
- package/src/components/cnpg/CNPGPooling.tsx +97 -40
- package/src/components/cnpg/CNPGProtection.test.tsx +145 -0
- package/src/components/cnpg/CNPGProtection.tsx +230 -124
- package/src/components/cnpg/CNPGReplicationTab.test.tsx +87 -0
- package/src/components/cnpg/CNPGReplicationTab.tsx +212 -0
- package/src/components/cnpg/CNPGReplicationView.tsx +264 -0
- package/src/components/cnpg/CNPGStorage.test.tsx +140 -0
- package/src/components/cnpg/CNPGStorage.tsx +619 -0
- package/src/components/cnpg/CNPGSummaryHost.overview.test.tsx +44 -0
- package/src/components/cnpg/CNPGSummaryHost.test.tsx +23 -0
- package/src/components/cnpg/CNPGSummaryHost.tsx +228 -54
- package/src/components/cnpg/CNPGTrends.denied.test.tsx +33 -0
- package/src/components/cnpg/CNPGTrends.missing.test.tsx +69 -0
- package/src/components/cnpg/CNPGTrends.test.ts +38 -0
- package/src/components/cnpg/CNPGTrends.tsx +702 -0
- package/src/components/cnpg/CNPGView.tsx +34 -21
- package/src/components/cnpg/CNPGWorkspace.compatibility.test.tsx +90 -0
- package/src/components/cnpg/actions/CNPGClusterActions.dialog.test.tsx +79 -0
- package/src/components/cnpg/actions/CNPGClusterActions.test.tsx +142 -0
- package/src/components/cnpg/actions/CNPGClusterActions.tsx +853 -0
- package/src/components/cnpg/actions/CNPGConnectButton.read.test.tsx +40 -0
- package/src/components/cnpg/actions/CNPGConnectButton.test.ts +25 -0
- package/src/components/cnpg/actions/CNPGConnectButton.tsx +131 -0
- package/src/components/cnpg/actions/CNPGDestroyInstanceDialog.tsx +185 -0
- package/src/components/cnpg/actions/CNPGInstanceActions.test.tsx +38 -0
- package/src/components/cnpg/actions/CNPGInstanceActions.tsx +74 -0
- package/src/components/cnpg/actions/CNPGMaintenanceBanner.tsx +53 -0
- package/src/components/cnpg/actions/CNPGPoolerActions.test.tsx +35 -0
- package/src/components/cnpg/actions/CNPGPoolerActions.tsx +79 -0
- package/src/components/cnpg/actions/CNPGReportDialog.test.tsx +12 -0
- package/src/components/cnpg/actions/CNPGReportDialog.tsx +91 -0
- package/src/components/cnpg/actions/CNPGScheduleActions.run.test.tsx +58 -0
- package/src/components/cnpg/actions/CNPGScheduleActions.test.tsx +54 -0
- package/src/components/cnpg/actions/CNPGScheduleActions.tsx +208 -0
- package/src/components/cnpg/actions/actionModel.test.ts +111 -0
- package/src/components/cnpg/actions/actionModel.ts +127 -0
- package/src/components/cnpg/actions/renderCNPGHeaderActions.tsx +60 -0
- package/src/components/cnpg/actions/useCNPGWriteGuard.tsx +58 -0
- package/src/components/cnpg/actions/useOpenCNPGPsql.ts +22 -0
- package/src/components/cnpg/archivingRepair.test.ts +104 -0
- package/src/components/cnpg/archivingRepair.ts +145 -0
- package/src/components/cnpg/baseBackup.test.ts +36 -0
- package/src/components/cnpg/baseBackup.ts +36 -0
- package/src/components/cnpg/blocking.test.ts +59 -0
- package/src/components/cnpg/blocking.ts +131 -0
- package/src/components/cnpg/coverageText.test.ts +27 -0
- package/src/components/cnpg/creation/CNPGTargetFields.tsx +110 -0
- package/src/components/cnpg/creation/targetModel.test.ts +137 -0
- package/src/components/cnpg/creation/targetModel.ts +149 -0
- package/src/components/cnpg/fleetStatus.test.ts +46 -0
- package/src/components/cnpg/fleetStatus.ts +43 -0
- package/src/components/cnpg/formFields.ts +2 -0
- package/src/components/cnpg/grantText.test.tsx +11 -0
- package/src/components/cnpg/host.test.tsx +24 -0
- package/src/components/cnpg/host.tsx +101 -0
- package/src/components/cnpg/inspectModel.test.ts +120 -0
- package/src/components/cnpg/inspectModel.ts +140 -0
- package/src/components/cnpg/logicalSlots.test.ts +30 -0
- package/src/components/cnpg/logicalSlots.ts +29 -0
- package/src/components/cnpg/lsn.test.ts +40 -0
- package/src/components/cnpg/lsn.ts +43 -0
- package/src/components/cnpg/operations/CNPGFleetOperation.tsx +30 -0
- package/src/components/cnpg/operations/CNPGOperationTracker.test.tsx +247 -0
- package/src/components/cnpg/operations/CNPGOperationTracker.tsx +255 -0
- package/src/components/cnpg/operations/model.test.ts +510 -0
- package/src/components/cnpg/operations/model.ts +499 -0
- package/src/components/cnpg/operations/presentation.test.ts +80 -0
- package/src/components/cnpg/operations/presentation.ts +84 -0
- package/src/components/cnpg/operations/store.ts +98 -0
- package/src/components/cnpg/operatorStatus.test.ts +134 -0
- package/src/components/cnpg/operatorStatus.ts +194 -0
- package/src/components/cnpg/paths.test.ts +54 -0
- package/src/components/cnpg/paths.ts +47 -4
- package/src/components/cnpg/protection/CNPGAttachArchiveDialog.tsx +185 -0
- package/src/components/cnpg/protection/CNPGCreateScheduleDialog.tsx +196 -0
- package/src/components/cnpg/protection/CNPGProtectionSetup.test.tsx +121 -0
- package/src/components/cnpg/protection/CNPGProtectionSetup.tsx +452 -0
- package/src/components/cnpg/protection/CNPGScheduleInput.tsx +115 -0
- package/src/components/cnpg/protection/CNPGScheduleRepairButton.tsx +120 -0
- package/src/components/cnpg/protection/scheduleModel.test.ts +19 -0
- package/src/components/cnpg/protection/scheduleModel.ts +16 -0
- package/src/components/cnpg/recovery/CNPGRecoveryPoint.tsx +220 -0
- package/src/components/cnpg/recovery/CNPGRestoreButton.test.tsx +29 -0
- package/src/components/cnpg/recovery/CNPGRestoreButton.tsx +26 -0
- package/src/components/cnpg/recovery/CNPGRestoreChecks.test.tsx +23 -0
- package/src/components/cnpg/recovery/CNPGRestoreChecks.tsx +153 -0
- package/src/components/cnpg/recovery/CNPGRestoreDialog.back.test.tsx +149 -0
- package/src/components/cnpg/recovery/CNPGRestoreDialog.test.tsx +46 -0
- package/src/components/cnpg/recovery/CNPGRestoreDialog.tsx +333 -0
- package/src/components/cnpg/recovery/CNPGRestoreProgress.tsx +182 -0
- package/src/components/cnpg/recovery/CNPGRestoreValidation.tsx +216 -0
- package/src/components/cnpg/recovery/restoreModel.test.ts +709 -0
- package/src/components/cnpg/recovery/restoreModel.ts +685 -0
- package/src/components/cnpg/recovery/restoreOperation.ts +56 -0
- package/src/components/cnpg/refresh.test.ts +38 -0
- package/src/components/cnpg/refresh.ts +17 -0
- package/src/components/cnpg/refreshNotice.test.tsx +29 -0
- package/src/components/cnpg/routes.test.ts +34 -3
- package/src/components/cnpg/routes.ts +60 -48
- package/src/components/cnpg/runtimeAssessment.ts +198 -0
- package/src/components/cnpg/runtimeModel.test.ts +175 -0
- package/src/components/cnpg/runtimeModel.ts +196 -0
- package/src/components/cnpg/runtimeParts.test.tsx +17 -0
- package/src/components/cnpg/runtimeParts.tsx +79 -0
- package/src/components/cnpg/shared.test.tsx +59 -0
- package/src/components/cnpg/shared.tsx +100 -182
- package/src/components/cnpg/storageModel.test.ts +107 -0
- package/src/components/cnpg/storageModel.ts +101 -0
- package/src/components/cnpg/trendSamples.test.ts +148 -0
- package/src/components/cnpg/trendSamples.ts +222 -0
- package/src/components/cnpg/useCNPGClusterAssessment.ts +30 -0
- package/src/components/cnpg/useCNPGKubectlContext.test.tsx +45 -0
- package/src/components/cnpg/useCNPGKubectlContext.ts +13 -0
- package/src/components/cnpg/useCNPGNavigate.test.tsx +58 -0
- package/src/components/cnpg/useCNPGNavigate.ts +21 -0
- package/src/components/cnpg/useCNPGPoolerLive.ts +46 -0
- package/src/components/cnpg/useCNPGScreenParams.ts +24 -0
- package/src/components/cnpg/useCNPGSidebarWorkspace.ts +66 -16
- package/src/components/cnpg/workspaceRoute.ts +23 -0
- package/src/components/cost/ApplicationCostTab.test.ts +7 -0
- package/src/components/cost/ApplicationCostTab.tsx +7 -7
- package/src/components/cost/CostView.test.ts +2 -1
- package/src/components/cost/CostView.tsx +0 -2
- package/src/components/cost/WorkloadCostTab.tsx +2 -4
- package/src/components/cost/errors.ts +3 -1
- package/src/components/cost/source.test.ts +10 -0
- package/src/components/cost/source.ts +5 -1
- package/src/components/diagnose/AgentSetupNotice.test.tsx +58 -0
- package/src/components/diagnose/AgentSetupNotice.tsx +142 -51
- package/src/components/diagnose/ApplyDialog.ack.test.tsx +47 -0
- package/src/components/diagnose/ApplyDialog.test.tsx +59 -6
- package/src/components/diagnose/ApplyDialog.tsx +26 -28
- package/src/components/diagnose/DiagnoseContext.test.ts +43 -0
- package/src/components/diagnose/DiagnoseContext.tsx +136 -54
- package/src/components/diagnose/DiagnoseSurface.tsx +16 -3
- package/src/components/diagnose/InvestigationView.tsx +39 -2
- package/src/components/diagnose/LocalDiagnoseAction.tsx +40 -20
- package/src/components/diagnose/agentCatalog.ts +2 -2
- package/src/components/diagnose/diagnoseEvidenceTypes.ts +2 -0
- package/src/components/diagnose/investigationEvidence/adapters/issues.ts +8 -0
- package/src/components/diagnose/investigationEvidence/projection.test.ts +16 -0
- package/src/components/dock/BottomDock.tsx +2 -0
- package/src/components/dock/TerminalTab.test.tsx +17 -0
- package/src/components/dock/TerminalTab.tsx +7 -3
- package/src/components/dock/TrafficFlowListTab.tsx +27 -6
- package/src/components/execution/BatchExecutionView.tsx +11 -11
- package/src/components/gitops/ArgoResourceDiffLoader.tsx +4 -2
- package/src/components/gitops/GitOpsView.tsx +16 -23
- package/src/components/helm/ChartBrowser.tsx +3 -3
- package/src/components/helm/HelmCompareRoute.test.tsx +59 -0
- package/src/components/helm/HelmCompareRoute.tsx +57 -26
- package/src/components/helm/HelmReleaseDrawer.test.ts +55 -3
- package/src/components/helm/HelmReleaseDrawer.tsx +50 -12
- package/src/components/helm/RevisionHistory.tsx +3 -2
- package/src/components/helm/TrackChartSourceDialog.render.test.tsx +120 -0
- package/src/components/helm/TrackChartSourceDialog.test.ts +50 -0
- package/src/components/helm/TrackChartSourceDialog.tsx +153 -176
- package/src/components/helm/ValuesViewer.test.tsx +301 -0
- package/src/components/helm/ValuesViewer.tsx +169 -78
- package/src/components/home/ActivitySummary.tsx +2 -1
- package/src/components/home/ClusterHealthCard.tsx +7 -0
- package/src/components/home/HomeView.tsx +20 -9
- package/src/components/home/ProblemsPanel.test.tsx +81 -0
- package/src/components/home/TopologyPreview.tsx +2 -2
- package/src/components/home/mcpToolCatalog.ts +1 -1
- package/src/components/issues/IssuesPane.render.test.tsx +122 -0
- package/src/components/issues/IssuesPane.test.ts +65 -1
- package/src/components/issues/IssuesPane.tsx +188 -25
- package/src/components/issues/uncheckedSources.ts +21 -0
- package/src/components/resource/PVCUsageBar.render.test.tsx +32 -0
- package/src/components/resource/PVCUsageBar.test.tsx +24 -0
- package/src/components/resource/PVCUsageBar.tsx +15 -6
- package/src/components/resource/WorkloadMetricsHelpDialog.test.tsx +3 -1
- package/src/components/resource/WorkloadMetricsHelpDialog.tsx +1 -1
- package/src/components/resources/CompositeRenderer.tsx +4 -4
- package/src/components/resources/ResourceDetailDrawer.tsx +2 -2
- package/src/components/resources/ResourcesView.tsx +26 -12
- package/src/components/resources/renderers/KyvernoPolicyQueued.tsx +2 -1
- package/src/components/resources/renderers/NodeRenderer.tsx +7 -1
- package/src/components/resources/renderers/ServiceRenderer.test.tsx +81 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +19 -14
- package/src/components/settings/LocalIntegrationStatus.test.tsx +52 -0
- package/src/components/settings/LocalIntegrationStatus.tsx +3 -4
- package/src/components/settings/SettingsDialog.tsx +150 -34
- package/src/components/shared/LargeClusterNamespacePicker.tsx +11 -5
- package/src/components/timeline/TimelineList.tsx +25 -24
- package/src/components/timeline/TimelineLoadError.test.tsx +62 -0
- package/src/components/timeline/TimelineLoadError.tsx +96 -0
- package/src/components/timeline/TimelineRetentionNote.test.tsx +49 -0
- package/src/components/timeline/TimelineRetentionNote.tsx +20 -0
- package/src/components/timeline/TimelineView.tsx +25 -45
- package/src/components/timeline/ViewModeToggle.tsx +20 -0
- package/src/components/timeline/useStickyError.test.tsx +47 -0
- package/src/components/timeline/useStickyError.ts +15 -0
- package/src/components/traffic/TrafficConnectionsTable.tsx +144 -0
- package/src/components/traffic/TrafficFilterSidebar.tsx +22 -1
- package/src/components/traffic/TrafficFlowListContext.tsx +12 -4
- package/src/components/traffic/TrafficFocusSearch.tsx +172 -0
- package/src/components/traffic/TrafficGraph.tsx +109 -26
- package/src/components/traffic/TrafficGraphTooLarge.tsx +150 -0
- package/src/components/traffic/TrafficView.tsx +582 -149
- package/src/components/traffic/trafficFilters.test.ts +398 -1
- package/src/components/traffic/trafficFilters.ts +408 -0
- package/src/components/ui/DiagnosticsOverlay.tsx +34 -2
- package/src/components/ui/Markdown.test.tsx +65 -0
- package/src/components/ui/Markdown.tsx +144 -127
- package/src/components/whats-new/releaseNotes.ts +63 -1
- package/src/components/workload/WorkloadView.test.ts +11 -1
- package/src/components/workload/WorkloadView.tsx +162 -529
- package/src/components/workspace/layout.test.tsx +29 -0
- package/src/components/workspace/layout.tsx +259 -0
- package/src/components/workspace/table.ts +7 -0
- package/src/context/TimelineCustomization.tsx +15 -0
- package/src/contexts/CapabilitiesContext.tsx +1 -1
- package/src/hooks/useGitOpsWriteGuard.test.tsx +457 -0
- package/src/hooks/useGitOpsWriteGuard.ts +193 -0
- package/src/hooks/useResolvedGitOpsOwner.ts +256 -0
- package/src/integrations/resourceHost.test.tsx +55 -0
- package/src/integrations/resourceHost.ts +99 -0
- package/src/integrations/resourceHosts.test.tsx +141 -0
- package/src/integrations/resourceHosts.tsx +119 -0
- package/src/integrations/workspaceRoute.ts +20 -0
- package/src/integrations/workspaceRoutes.test.ts +40 -0
- package/src/integrations/workspaceRoutes.ts +22 -0
- package/src/integrations/workspaceScreens.tsx +14 -0
- package/src/utils/drawer-trail.ts +35 -0
- package/src/utils/kindFilterStore.test.ts +147 -0
- package/src/utils/kindFilterStore.ts +91 -0
- package/src/utils/navigation.test.ts +13 -1
- package/src/utils/navigation.ts +39 -2
- package/src/utils/page-links.ts +21 -0
- package/src/utils/tabStorage.ts +40 -0
- package/src/utils/viewMemory.test.ts +78 -0
- package/src/utils/viewMemory.ts +54 -0
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { JobRenderer, JobSetRenderer } from '../resources/renderers/JobAdmissionRenderers'
|
|
3
|
-
import { RayClusterRenderer } from '../resources/renderers/RayClusterRenderer'
|
|
4
|
-
import { RayServiceRenderer } from '../resources/renderers/RayServiceRenderer'
|
|
5
|
-
import { KueueWorkloadRenderer } from '../resources/renderers/KueueWorkloadRenderer'
|
|
1
|
+
import { decorateResourceDiagnose, resourceDetailRedirect, resourceDetailSlots, resourceLogs, resourceRendererOverrides } from '../../integrations/resourceHosts'
|
|
6
2
|
import { useMemo, useEffect, useCallback, useRef, useState, type ReactNode } from 'react'
|
|
7
3
|
import { useQueries, useQueryClient } from '@tanstack/react-query'
|
|
8
4
|
import { Navigate, useNavigate, useLocation, useSearchParams } from 'react-router-dom'
|
|
@@ -11,34 +7,38 @@ import { clsx } from 'clsx'
|
|
|
11
7
|
import { Terminal, Stethoscope } from 'lucide-react'
|
|
12
8
|
import {
|
|
13
9
|
WorkloadView as BaseWorkloadView,
|
|
14
|
-
isApiGroup,
|
|
15
10
|
EditableYamlView,
|
|
16
11
|
FetchResult,
|
|
17
12
|
Section,
|
|
18
13
|
type WorkloadTabType,
|
|
19
14
|
type WorkloadExtraTab,
|
|
20
|
-
type RendererOverrides,
|
|
21
15
|
type GitOpsOwnerRef,
|
|
22
|
-
type GitOpsStatus,
|
|
23
16
|
type HelmOwnerRef,
|
|
24
17
|
type AppRow,
|
|
25
18
|
type ResourceOwnershipContext,
|
|
26
19
|
type ServingResourceDetail,
|
|
27
20
|
type AuditFinding,
|
|
21
|
+
dedupeResourceRefs,
|
|
28
22
|
gitOpsRouteForOwner,
|
|
29
23
|
gitOpsOwnerFromRelationships,
|
|
30
|
-
getGitOpsResourceStatus,
|
|
31
24
|
isDiagnoseKind,
|
|
32
25
|
isRolloutKind,
|
|
33
26
|
canSetWorkloadImages,
|
|
34
27
|
isCoreBatchJob,
|
|
35
|
-
type
|
|
28
|
+
type SetImageOwnership,
|
|
29
|
+
SET_IMAGE_WRITES,
|
|
30
|
+
setImageWrites,
|
|
31
|
+
setImageSelectionKey,
|
|
32
|
+
gitOpsWriteEvidencePaths,
|
|
33
|
+
type SetImageSelection,
|
|
36
34
|
type WorkloadImageTarget,
|
|
37
35
|
} from '@skyhook-io/k8s-ui'
|
|
38
36
|
import type { ServicePortRenderProps } from '@skyhook-io/k8s-ui/components/resources/renderers/ServiceRenderer'
|
|
39
37
|
import { isJobSetV1Alpha2 } from '@skyhook-io/k8s-ui/components/resources/resource-utils-jobset-lws'
|
|
40
38
|
import type { SelectedResource, ResourceRef, Relationships, ResourceWithRelationships } from '../../types'
|
|
41
39
|
import { useHistoryPaging } from './historyPaging'
|
|
40
|
+
import { findInheritedGitOpsLookupRef, useResolvedGitOpsOwner } from '../../hooks/useResolvedGitOpsOwner'
|
|
41
|
+
import { useGitOpsWriteGuard } from '../../hooks/useGitOpsWriteGuard'
|
|
42
42
|
import {
|
|
43
43
|
kindToPlural,
|
|
44
44
|
kindToPluralWithGroup,
|
|
@@ -97,7 +97,8 @@ import { RightsizingPanel } from '../resource/RightsizingStrip'
|
|
|
97
97
|
import { WorkloadCostTab } from '../cost/WorkloadCostTab'
|
|
98
98
|
import { isOpenCostWorkloadKind } from '../cost/kinds'
|
|
99
99
|
import { isRadarFeatureUnsupported } from '../../api/radarFeatures'
|
|
100
|
-
import {
|
|
100
|
+
import { useTrafficAvailable } from '../../api/traffic'
|
|
101
|
+
import { useResourceAudit, useResourceIssues, useTrace, fetchTraceWithProbes, fetchInClusterCapability, runInClusterMerged } from '../../api/client'
|
|
101
102
|
import { AuditAlerts, getRadarUpgradeRequirement, ResourceIssuesSection, ReachabilityView, TraceSummary, InClusterConsentDialog, traceFingerprint, staticPollUnreliable, summarizeInClusterTests, type Trace as NetworkTrace, type InClusterCapability, inClusterConsentGiven, consentRequestRows } from '@skyhook-io/k8s-ui'
|
|
102
103
|
import { WorkloadLogsViewer } from '../logs/WorkloadLogsViewer'
|
|
103
104
|
import { ScheduledWorkloadLogsViewer } from '../logs/ScheduledWorkloadLogsViewer'
|
|
@@ -121,38 +122,7 @@ import {
|
|
|
121
122
|
defaultPathForPort,
|
|
122
123
|
} from '../curl/ServiceCurlButton'
|
|
123
124
|
import { useToast } from '../ui/Toast'
|
|
124
|
-
import { Tooltip } from '../ui/Tooltip'
|
|
125
|
-
import { PodRenderer } from '../resources/renderers/PodRenderer'
|
|
126
|
-
import { KarpenterNodePoolRenderer } from '../resources/renderers/KarpenterNodePoolRenderer'
|
|
127
|
-
import { NodeRenderer } from '../resources/renderers/NodeRenderer'
|
|
128
|
-
import { ServiceRenderer } from '../resources/renderers/ServiceRenderer'
|
|
129
|
-
import { WorkloadRenderer } from '../resources/renderers/WorkloadRenderer'
|
|
130
|
-
import { CompositeRenderer } from '../resources/CompositeRenderer'
|
|
131
|
-
import { ServiceAccountRenderer } from '../resources/renderers/ServiceAccountRenderer'
|
|
132
|
-
import { RoleRenderer } from '../resources/renderers/RoleRenderer'
|
|
133
|
-
import { RoleBindingRenderer } from '../resources/renderers/RoleBindingRenderer'
|
|
134
|
-
import { NamespaceRenderer } from '../resources/renderers/NamespaceRenderer'
|
|
135
|
-
import { CAPIClusterRenderer } from '../resources/renderers/CAPIClusterRenderer'
|
|
136
|
-
import { HPARenderer } from '../resources/renderers/HPARenderer'
|
|
137
|
-
import { PVCRenderer } from '../resources/renderers/PVCRenderer'
|
|
138
|
-
import { RolloutRenderer } from '../resources/renderers/RolloutRenderer'
|
|
139
|
-
import { KyvernoPolicyCoverage } from '../resources/renderers/KyvernoPolicyCoverage'
|
|
140
|
-
import { KyvernoPolicyQueued } from '../resources/renderers/KyvernoPolicyQueued'
|
|
141
|
-
import { CNPGObjectStoreRenderer } from '../resources/renderers/CNPGObjectStoreRenderer'
|
|
142
|
-
import { VeleroBSLRenderer } from '../resources/renderers/VeleroBSLRenderer'
|
|
143
|
-
import { VeleroBackupRenderer } from '../resources/renderers/VeleroBackupRenderer'
|
|
144
|
-
import { VeleroRestoreRenderer } from '../resources/renderers/VeleroRestoreRenderer'
|
|
145
|
-
import { CNPGClusterRenderer } from '../resources/renderers/CNPGClusterRenderer'
|
|
146
|
-
import { CNPGImageCatalogRenderer } from '../resources/renderers/CNPGImageCatalogRenderer'
|
|
147
|
-
import {
|
|
148
|
-
CNPGDatabaseRenderer,
|
|
149
|
-
CNPGPublicationRenderer,
|
|
150
|
-
CNPGSubscriptionRenderer,
|
|
151
|
-
} from '../resources/renderers/CNPGDeclarativeRenderer'
|
|
152
125
|
import { CreateResourceDialog } from '../shared/CreateResourceDialog'
|
|
153
|
-
import { renderCNPGSummary } from '../cnpg/CNPGSummaryHost'
|
|
154
|
-
import { CNPGClusterLogs } from '../cnpg/CNPGClusterLogs'
|
|
155
|
-
import { cnpgDetailKindFor, cnpgDetailPath } from '../cnpg/routes'
|
|
156
126
|
import { cleanYamlForDuplicate } from '../../utils/skeleton-yaml'
|
|
157
127
|
import { useDesktopDownload } from '../../hooks/useDesktopDownload'
|
|
158
128
|
import { useCompareLauncher } from '../compare/useCompareLauncher'
|
|
@@ -179,41 +149,6 @@ export function supportsBatchExecution(kind: string, apiKind: string, group?: st
|
|
|
179
149
|
return true
|
|
180
150
|
}
|
|
181
151
|
|
|
182
|
-
// Stable reference — web renderer wrappers inject platform hooks internally
|
|
183
|
-
const rendererOverrides: RendererOverrides = {
|
|
184
|
-
RayJobRenderer,
|
|
185
|
-
JobRenderer,
|
|
186
|
-
JobSetRenderer,
|
|
187
|
-
RayServiceRenderer,
|
|
188
|
-
RayClusterRenderer,
|
|
189
|
-
KueueWorkloadRenderer,
|
|
190
|
-
CAPIClusterRenderer,
|
|
191
|
-
PodRenderer,
|
|
192
|
-
KarpenterNodePoolRenderer,
|
|
193
|
-
NodeRenderer,
|
|
194
|
-
ServiceRenderer,
|
|
195
|
-
WorkloadRenderer,
|
|
196
|
-
CompositeRenderer,
|
|
197
|
-
ServiceAccountRenderer,
|
|
198
|
-
RoleRenderer,
|
|
199
|
-
RoleBindingRenderer,
|
|
200
|
-
NamespaceRenderer,
|
|
201
|
-
HPARenderer,
|
|
202
|
-
PVCRenderer,
|
|
203
|
-
RolloutRenderer,
|
|
204
|
-
KyvernoPolicyCoverage,
|
|
205
|
-
KyvernoPolicyQueued,
|
|
206
|
-
CNPGObjectStoreRenderer,
|
|
207
|
-
VeleroBSLRenderer,
|
|
208
|
-
VeleroBackupRenderer,
|
|
209
|
-
VeleroRestoreRenderer,
|
|
210
|
-
CNPGClusterRenderer,
|
|
211
|
-
CNPGDatabaseRenderer,
|
|
212
|
-
CNPGPublicationRenderer,
|
|
213
|
-
CNPGSubscriptionRenderer,
|
|
214
|
-
CNPGImageCatalogRenderer,
|
|
215
|
-
}
|
|
216
|
-
|
|
217
152
|
// ============================================================================
|
|
218
153
|
// ROUTE WRAPPER — parses kind/ns/name from URL
|
|
219
154
|
// ============================================================================
|
|
@@ -282,16 +217,8 @@ export function WorkloadViewRoute({ onNavigateToResource }: WorkloadViewRoutePro
|
|
|
282
217
|
)
|
|
283
218
|
}
|
|
284
219
|
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
287
|
-
const params = new URLSearchParams(searchParams)
|
|
288
|
-
params.delete('apiGroup')
|
|
289
|
-
const tab = params.get('tab')
|
|
290
|
-
if (cnpgPlural === 'clusters' && (tab === 'timeline' || tab === 'events')) params.set('tab', 'activity')
|
|
291
|
-
const base = cnpgDetailPath({ plural: cnpgPlural, namespace, name })
|
|
292
|
-
const qs = params.toString()
|
|
293
|
-
return <Navigate replace to={qs ? `${base}?${qs}` : base} state={location.state} />
|
|
294
|
-
}
|
|
220
|
+
const detailPath = resourceDetailRedirect({ kind, group, namespace, name }, searchParams)
|
|
221
|
+
if (detailPath) return <Navigate replace to={detailPath} state={location.state} />
|
|
295
222
|
|
|
296
223
|
return (
|
|
297
224
|
<WorkloadView
|
|
@@ -305,6 +232,10 @@ export function WorkloadViewRoute({ onNavigateToResource }: WorkloadViewRoutePro
|
|
|
305
232
|
)
|
|
306
233
|
}
|
|
307
234
|
|
|
235
|
+
// The kinds the traffic map draws as a node of their own: those that own
|
|
236
|
+
// pods, which it groups under them.
|
|
237
|
+
const TRAFFIC_WORKLOAD_PLURALS = new Set(['deployments', 'statefulsets', 'daemonsets', 'rollouts', 'cronjobs', 'jobs'])
|
|
238
|
+
|
|
308
239
|
// ============================================================================
|
|
309
240
|
// WORKLOAD VIEW WRAPPER — injects data fetching hooks
|
|
310
241
|
// ============================================================================
|
|
@@ -330,6 +261,16 @@ interface WorkloadViewProps {
|
|
|
330
261
|
pushTabHistory?: boolean
|
|
331
262
|
breadcrumb?: ReactNode
|
|
332
263
|
extraTabs?: WorkloadExtraTab[]
|
|
264
|
+
tabOrder?: string[]
|
|
265
|
+
subheader?: ReactNode
|
|
266
|
+
specTab?: { label?: string; icon?: ReactNode; lead?: ReactNode; render?: () => ReactNode }
|
|
267
|
+
titlePrefix?: ReactNode
|
|
268
|
+
inlineBadges?: boolean
|
|
269
|
+
namespaceNote?: ReactNode
|
|
270
|
+
renderStatusBadge?: (resource: any) => ReactNode
|
|
271
|
+
statusNote?: ReactNode
|
|
272
|
+
onRefresh?: () => Promise<unknown>
|
|
273
|
+
hideKindBadge?: boolean
|
|
333
274
|
}
|
|
334
275
|
|
|
335
276
|
interface ImageTargetOwnershipContext {
|
|
@@ -340,7 +281,6 @@ interface ImageTargetOwnershipContext {
|
|
|
340
281
|
}
|
|
341
282
|
target: WorkloadImageTarget
|
|
342
283
|
response: ResourceWithRelationships<Record<string, unknown>>
|
|
343
|
-
inheritedResponse?: ResourceWithRelationships<Record<string, unknown>>
|
|
344
284
|
}
|
|
345
285
|
|
|
346
286
|
function useActionsBarProps(
|
|
@@ -453,7 +393,7 @@ function useActionsBarProps(
|
|
|
453
393
|
name: string
|
|
454
394
|
className?: string
|
|
455
395
|
}) => <PortForwardButton type={type} namespace={ns} name={n} className={className} />,
|
|
456
|
-
renderDiagnose,
|
|
396
|
+
renderDiagnose: decorateResourceDiagnose(renderDiagnose),
|
|
457
397
|
onDelete: (
|
|
458
398
|
params: Parameters<typeof deleteMutation.mutate>[0],
|
|
459
399
|
callbacks?: { onSuccess?: () => void },
|
|
@@ -461,7 +401,8 @@ function useActionsBarProps(
|
|
|
461
401
|
isDeleting: deleteMutation.isPending,
|
|
462
402
|
cascadeDependents: cascadePreview?.dependents,
|
|
463
403
|
cascadeLoading,
|
|
464
|
-
cascadeRootResolved:
|
|
404
|
+
cascadeRootResolved: cascadePreview?.rootResolved,
|
|
405
|
+
cascadePreviewFailed: Boolean(cascadeError),
|
|
465
406
|
onRestart: rolloutAllows('restart')
|
|
466
407
|
? (params: Parameters<typeof restartWorkloadMutation.mutate>[0]) =>
|
|
467
408
|
restartWorkloadMutation.mutate(params)
|
|
@@ -560,6 +501,8 @@ export function WorkloadView({
|
|
|
560
501
|
const [imageTargetOwnership, setImageTargetOwnership] =
|
|
561
502
|
useState<ImageTargetOwnershipContext | null>(null)
|
|
562
503
|
const imageOwnershipRequestRef = useRef(0)
|
|
504
|
+
// The image guard's evidence is read only once the user opens the dialog.
|
|
505
|
+
const [imageGuardRequested, setImageGuardRequested] = useState(false)
|
|
563
506
|
|
|
564
507
|
// Tab state from URL query param — migrate legacy tab names
|
|
565
508
|
const rawTab = searchParams.get('tab')
|
|
@@ -633,9 +576,11 @@ export function WorkloadView({
|
|
|
633
576
|
() => (isDiagnoseKind(apiKind, effectiveGroup) ? [] : (relationships?.services ?? [])),
|
|
634
577
|
[apiKind, effectiveGroup, relationships],
|
|
635
578
|
)
|
|
579
|
+
const onRefresh = rest.onRefresh
|
|
636
580
|
const refetchResourceAndRuns = useCallback(async () => {
|
|
637
581
|
await Promise.all([
|
|
638
582
|
refetchResource(),
|
|
583
|
+
onRefresh?.(),
|
|
639
584
|
...(apiKind === 'rayjobs' && effectiveGroup === 'ray.io' ? [
|
|
640
585
|
queryClient.refetchQueries({ queryKey: ['resource', 'jobs', namespace, name, 'batch'], type: 'active' }),
|
|
641
586
|
...(resource?.spec?.clusterSelector?.['ray.io/cluster'] ? [queryClient.refetchQueries({ queryKey: ['resource', 'rayclusters', namespace, resource.spec.clusterSelector['ray.io/cluster'], 'ray.io'], type: 'active' })] : []),
|
|
@@ -649,7 +594,7 @@ export function WorkloadView({
|
|
|
649
594
|
...(apiKind === 'rayclusters' ? { type: 'active' as const } : {}),
|
|
650
595
|
}),
|
|
651
596
|
])
|
|
652
|
-
}, [apiKind, effectiveGroup, name, namespace, queryClient, refetchResource, resource?.spec?.clusterSelector])
|
|
597
|
+
}, [apiKind, effectiveGroup, name, namespace, queryClient, refetchResource, resource?.spec?.clusterSelector, onRefresh])
|
|
653
598
|
const podWorkloadOwner = useMemo(
|
|
654
599
|
() => podWorkloadOwnerFromRelationships(apiKind, namespace, relationships, resource),
|
|
655
600
|
[apiKind, namespace, relationships, resource],
|
|
@@ -674,125 +619,33 @@ export function WorkloadView({
|
|
|
674
619
|
: null,
|
|
675
620
|
[apiKind, imageTargetOwnership, name, namespace],
|
|
676
621
|
)
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
()
|
|
703
|
-
nativeHelmOwnerFromRelationships(relationships, resource?.metadata?.namespace ?? namespace),
|
|
704
|
-
[relationships, resource?.metadata?.namespace, namespace],
|
|
705
|
-
)
|
|
706
|
-
const inheritedHelmOwner = useMemo(
|
|
707
|
-
() =>
|
|
708
|
-
nativeHelmOwnerFromRelationships(
|
|
709
|
-
inheritedGitOpsResponse.data?.relationships,
|
|
710
|
-
inheritedGitOpsResponse.data?.resource?.metadata?.namespace ?? namespace,
|
|
711
|
-
),
|
|
712
|
-
[
|
|
713
|
-
inheritedGitOpsResponse.data?.relationships,
|
|
714
|
-
inheritedGitOpsResponse.data?.resource?.metadata?.namespace,
|
|
715
|
-
namespace,
|
|
716
|
-
],
|
|
717
|
-
)
|
|
718
|
-
const rawGitopsOwner = relationshipGitopsOwner ?? inheritedGitopsOwner
|
|
719
|
-
const gitOpsSourceResource = relationshipGitopsOwner
|
|
720
|
-
? resource
|
|
721
|
-
: inheritedGitOpsResponse.data?.resource
|
|
722
|
-
const helmOwner = relationshipHelmOwner ?? inheritedHelmOwner
|
|
723
|
-
const helmSourceResource = relationshipHelmOwner
|
|
724
|
-
? resource
|
|
725
|
-
: inheritedGitOpsResponse.data?.resource
|
|
726
|
-
const targetRelationshipGitopsOwner = useMemo(
|
|
727
|
-
() => gitOpsOwnerFromRelationships(activeImageTargetOwnership?.response.relationships),
|
|
728
|
-
[activeImageTargetOwnership?.response.relationships],
|
|
729
|
-
)
|
|
730
|
-
const targetInheritedGitopsOwner = useMemo(
|
|
731
|
-
() =>
|
|
732
|
-
gitOpsOwnerFromRelationships(
|
|
733
|
-
activeImageTargetOwnership?.inheritedResponse?.relationships,
|
|
734
|
-
),
|
|
735
|
-
[activeImageTargetOwnership?.inheritedResponse?.relationships],
|
|
736
|
-
)
|
|
737
|
-
const targetRawGitopsOwner = targetRelationshipGitopsOwner ?? targetInheritedGitopsOwner
|
|
738
|
-
const targetRelationshipHelmOwner = nativeHelmOwnerFromRelationships(
|
|
739
|
-
activeImageTargetOwnership?.response.relationships,
|
|
740
|
-
activeImageTargetOwnership?.target.namespace ?? namespace,
|
|
741
|
-
)
|
|
742
|
-
const targetInheritedHelmOwner = nativeHelmOwnerFromRelationships(
|
|
743
|
-
activeImageTargetOwnership?.inheritedResponse?.relationships,
|
|
744
|
-
activeImageTargetOwnership?.target.namespace ?? namespace,
|
|
745
|
-
)
|
|
746
|
-
const targetHelmOwner = targetRelationshipHelmOwner ?? targetInheritedHelmOwner
|
|
747
|
-
const shouldResolveArgoOwner =
|
|
748
|
-
(rawGitopsOwner?.tool === 'argocd' && !rawGitopsOwner.namespace) ||
|
|
749
|
-
(targetRawGitopsOwner?.tool === 'argocd' && !targetRawGitopsOwner.namespace)
|
|
750
|
-
const { data: argoApplications } = useResources<any>('applications', undefined, 'argoproj.io', {
|
|
751
|
-
enabled: shouldResolveArgoOwner,
|
|
622
|
+
const workloadOwnership = useResolvedGitOpsOwner({
|
|
623
|
+
kind: apiKind,
|
|
624
|
+
group: rest.group,
|
|
625
|
+
namespace,
|
|
626
|
+
name,
|
|
627
|
+
relationships,
|
|
628
|
+
resource,
|
|
629
|
+
})
|
|
630
|
+
const {
|
|
631
|
+
owner: gitopsOwner,
|
|
632
|
+
ownerObject: gitopsOwnerObject,
|
|
633
|
+
ownerVerified: gitOpsOwnerVerified,
|
|
634
|
+
ownerPending: gitOpsOwnerPending,
|
|
635
|
+
ownerSource: gitOpsOwnerSource,
|
|
636
|
+
ownerStatus: gitOpsOwnerStatus,
|
|
637
|
+
helmOwner,
|
|
638
|
+
helmSource: helmOwnerSource,
|
|
639
|
+
} = workloadOwnership
|
|
640
|
+
const imageTargetOwnershipResolution = useResolvedGitOpsOwner({
|
|
641
|
+
kind: activeImageTargetOwnership?.target.resource ?? '',
|
|
642
|
+
group: activeImageTargetOwnership?.target.group,
|
|
643
|
+
namespace: activeImageTargetOwnership?.target.namespace ?? '',
|
|
644
|
+
name: activeImageTargetOwnership?.target.name ?? '',
|
|
645
|
+
relationships: activeImageTargetOwnership?.response.relationships,
|
|
646
|
+
resource: activeImageTargetOwnership?.response.resource,
|
|
647
|
+
enabled: Boolean(activeImageTargetOwnership),
|
|
752
648
|
})
|
|
753
|
-
const gitopsOwner = useMemo(
|
|
754
|
-
() => resolveGitOpsOwner(rawGitopsOwner, argoApplications),
|
|
755
|
-
[rawGitopsOwner, argoApplications],
|
|
756
|
-
)
|
|
757
|
-
const targetGitopsOwner = useMemo(
|
|
758
|
-
() => resolveGitOpsOwner(targetRawGitopsOwner, argoApplications),
|
|
759
|
-
[argoApplications, targetRawGitopsOwner],
|
|
760
|
-
)
|
|
761
|
-
const gitopsOwnerGroup = gitopsOwner ? gitOpsOwnerGroup(gitopsOwner) : ''
|
|
762
|
-
const shouldFetchGitOpsOwner = Boolean(gitopsOwner?.namespace)
|
|
763
|
-
const gitopsOwnerQuery = useResource<any>(
|
|
764
|
-
shouldFetchGitOpsOwner ? gitopsOwner!.kind : '',
|
|
765
|
-
gitopsOwner?.namespace ?? '',
|
|
766
|
-
gitopsOwner?.name ?? '',
|
|
767
|
-
gitopsOwnerGroup,
|
|
768
|
-
)
|
|
769
|
-
const targetGitopsOwnerGroup = targetGitopsOwner ? gitOpsOwnerGroup(targetGitopsOwner) : ''
|
|
770
|
-
const shouldFetchTargetGitOpsOwner = Boolean(
|
|
771
|
-
activeImageTargetOwnership && targetGitopsOwner?.namespace,
|
|
772
|
-
)
|
|
773
|
-
const targetGitopsOwnerQuery = useResource<Record<string, unknown>>(
|
|
774
|
-
shouldFetchTargetGitOpsOwner ? targetGitopsOwner!.kind : '',
|
|
775
|
-
targetGitopsOwner?.namespace ?? '',
|
|
776
|
-
targetGitopsOwner?.name ?? '',
|
|
777
|
-
targetGitopsOwnerGroup,
|
|
778
|
-
{ enabled: shouldFetchTargetGitOpsOwner },
|
|
779
|
-
)
|
|
780
|
-
const gitOpsOwnerStatus = useMemo(
|
|
781
|
-
() => deriveGitOpsOwnerStatus(gitopsOwner, gitopsOwnerQuery.data),
|
|
782
|
-
[gitopsOwner, gitopsOwnerQuery.data],
|
|
783
|
-
)
|
|
784
|
-
const gitOpsOwnerVerified = Boolean(gitopsOwner?.namespace && gitopsOwnerQuery.data)
|
|
785
|
-
const gitOpsOwnerPending = Boolean(
|
|
786
|
-
gitopsOwner?.namespace && gitopsOwnerQuery.isLoading && !gitopsOwnerQuery.data,
|
|
787
|
-
)
|
|
788
|
-
const gitOpsOwnerSource = useMemo(
|
|
789
|
-
() => describeGitOpsOwnerSource(rawGitopsOwner, gitOpsSourceResource),
|
|
790
|
-
[rawGitopsOwner, gitOpsSourceResource],
|
|
791
|
-
)
|
|
792
|
-
const helmOwnerSource = useMemo(
|
|
793
|
-
() => describeHelmOwnerSource(helmOwner, helmSourceResource),
|
|
794
|
-
[helmOwner, helmSourceResource],
|
|
795
|
-
)
|
|
796
649
|
|
|
797
650
|
// Fetch topology for hierarchy building (only when expanded). Polled like
|
|
798
651
|
// useTrace's "drawer feeling live" pattern — without this, a resource
|
|
@@ -989,6 +842,23 @@ export function WorkloadView({
|
|
|
989
842
|
(path: string) => navigateRouter(path),
|
|
990
843
|
[navigateRouter],
|
|
991
844
|
)
|
|
845
|
+
// Offered for the kinds the traffic map draws as one node, and only once a
|
|
846
|
+
// traffic source is known to be available on this cluster. A Job something
|
|
847
|
+
// controls (a CronJob, a JobSet) is not one: the map groups its pods under
|
|
848
|
+
// that owner, so it is offered only once the Job is known to have none.
|
|
849
|
+
const isJob = apiKind.toLowerCase() === 'jobs'
|
|
850
|
+
const controlledJob = isJob && (!resource ||
|
|
851
|
+
(resource.metadata?.ownerReferences ?? []).some((ref: { controller?: boolean }) => ref.controller))
|
|
852
|
+
const trafficKind = !!namespace && TRAFFIC_WORKLOAD_PLURALS.has(apiKind.toLowerCase()) && !controlledJob
|
|
853
|
+
const trafficAvailable = useTrafficAvailable(trafficKind)
|
|
854
|
+
const openLiveTraffic = useCallback(() => {
|
|
855
|
+
const params = new URLSearchParams()
|
|
856
|
+
const namespaces = searchParams.get('namespaces')
|
|
857
|
+
if (namespaces) params.set('namespaces', namespaces)
|
|
858
|
+
params.set('focus', `${namespace}/${name}`)
|
|
859
|
+
navigateRouter({ pathname: '/traffic', search: params.toString() })
|
|
860
|
+
}, [navigateRouter, searchParams, namespace, name])
|
|
861
|
+
const offerLiveTraffic = trafficKind && trafficAvailable
|
|
992
862
|
// Drawer TraceSummary CTA → open the full resource view ON the Reachability tab.
|
|
993
863
|
// The generic onExpand navigates to the workload path but drops the query, so we
|
|
994
864
|
// navigate directly to that path WITH ?tab=reachability - the deeplink the
|
|
@@ -1013,6 +883,10 @@ export function WorkloadView({
|
|
|
1013
883
|
const loadImagesWithTargetOwnership = useCallback(
|
|
1014
884
|
async (params: { kind: string; namespace: string; name: string }) => {
|
|
1015
885
|
const request = ++imageOwnershipRequestRef.current
|
|
886
|
+
setImageGuardRequested(true)
|
|
887
|
+
// Each open and refresh re-reads the evidence: the owner's sync policy
|
|
888
|
+
// or ignore rules may have changed since the dialog last opened.
|
|
889
|
+
void queryClient.invalidateQueries({ queryKey: ['gitops-write-evidence'] })
|
|
1016
890
|
const inventory = await baseActionsBarProps.onLoadImages!(params)
|
|
1017
891
|
const targetDiffers =
|
|
1018
892
|
inventory.target.resource.toLowerCase() !== params.kind.toLowerCase() ||
|
|
@@ -1058,14 +932,17 @@ export function WorkloadView({
|
|
|
1058
932
|
group: inventory.target.group,
|
|
1059
933
|
},
|
|
1060
934
|
)
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
935
|
+
// Warm the cache the target's ownership resolution reads, so the
|
|
936
|
+
// dialog doesn't open on a still-pending inherited lookup. A failed
|
|
937
|
+
// read surfaces there as unknown ownership, not as a failed load.
|
|
938
|
+
if (inheritedRef) {
|
|
939
|
+
await fetchRelationships(
|
|
940
|
+
kindToPluralWithGroup(inheritedRef.kind, inheritedRef.group ?? ''),
|
|
941
|
+
inheritedRef.namespace,
|
|
942
|
+
inheritedRef.name,
|
|
943
|
+
inheritedRef.group,
|
|
944
|
+
).catch(() => undefined)
|
|
945
|
+
}
|
|
1069
946
|
if (request === imageOwnershipRequestRef.current) {
|
|
1070
947
|
setImageTargetOwnership({
|
|
1071
948
|
root: {
|
|
@@ -1075,52 +952,74 @@ export function WorkloadView({
|
|
|
1075
952
|
},
|
|
1076
953
|
target: inventory.target,
|
|
1077
954
|
response,
|
|
1078
|
-
inheritedResponse,
|
|
1079
955
|
})
|
|
1080
956
|
}
|
|
1081
957
|
return inventory
|
|
1082
958
|
},
|
|
1083
959
|
[baseActionsBarProps.onLoadImages, queryClient, setImageTargetOwnership],
|
|
1084
960
|
)
|
|
1085
|
-
const
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
961
|
+
const imageOwnershipSource = activeImageTargetOwnership ? imageTargetOwnershipResolution : workloadOwnership
|
|
962
|
+
const imageGuardTarget = activeImageTargetOwnership
|
|
963
|
+
? {
|
|
964
|
+
kind: activeImageTargetOwnership.target.kind,
|
|
965
|
+
group: activeImageTargetOwnership.target.group,
|
|
966
|
+
namespace: activeImageTargetOwnership.target.namespace,
|
|
967
|
+
name: activeImageTargetOwnership.target.name,
|
|
968
|
+
}
|
|
969
|
+
: { kind: resource?.kind ?? pluralToKind(apiKind), group: effectiveGroup ?? '', namespace, name }
|
|
970
|
+
// Once the dialog lists the containers, ask about each by name (so the
|
|
971
|
+
// evidence can't predate the list) and judge the ones being changed.
|
|
972
|
+
const [imageSelection, setImageSelection] = useState<SetImageSelection | null>(null)
|
|
973
|
+
const imageWrites = useMemo(
|
|
974
|
+
() =>
|
|
975
|
+
imageSelection
|
|
976
|
+
? setImageWrites(imageSelection.changed.length ? imageSelection.changed : imageSelection.containers)
|
|
977
|
+
: SET_IMAGE_WRITES,
|
|
978
|
+
[imageSelection],
|
|
979
|
+
)
|
|
980
|
+
const imageEvidencePaths = useMemo(
|
|
981
|
+
() => (imageSelection ? gitOpsWriteEvidencePaths(setImageWrites(imageSelection.containers)) : undefined),
|
|
982
|
+
[imageSelection],
|
|
983
|
+
)
|
|
984
|
+
const { guard: imageGuard, ownership: imageGuardOwnership } = useGitOpsWriteGuard({
|
|
985
|
+
target: imageGuardTarget,
|
|
986
|
+
writes: imageWrites,
|
|
987
|
+
evidencePaths: imageEvidencePaths,
|
|
988
|
+
ownership: imageOwnershipSource,
|
|
989
|
+
relationshipsUnavailable: activeImageTargetOwnership
|
|
990
|
+
? !activeImageTargetOwnership.response.relationships
|
|
991
|
+
: Boolean(resource) && !relationships,
|
|
992
|
+
enabled: imageGuardRequested,
|
|
993
|
+
})
|
|
994
|
+
const {
|
|
995
|
+
owner: imageOwner,
|
|
996
|
+
helmOwner: imageHelmOwner,
|
|
997
|
+
ownerVerified: imageOwnerVerified,
|
|
998
|
+
} = imageGuardOwnership
|
|
999
|
+
const imageOwnership = useMemo<SetImageOwnership | undefined>(() => {
|
|
1000
|
+
if (!imageGuard) return undefined
|
|
1001
|
+
const owner = imageOwner
|
|
1002
|
+
const helmOwner = imageHelmOwner
|
|
1003
|
+
return {
|
|
1004
|
+
guard: imageGuard,
|
|
1005
|
+
onSelectionChange: setImageSelection,
|
|
1006
|
+
selectionKey: setImageSelectionKey(imageSelection),
|
|
1007
|
+
onOpenOwner: owner
|
|
1008
|
+
? imageOwnerVerified
|
|
1009
|
+
? () => handleOpenGitOpsResource(owner)
|
|
1010
|
+
: undefined
|
|
1011
|
+
: helmOwner
|
|
1012
|
+
? () => handleOpenHelmRelease(helmOwner)
|
|
1103
1013
|
: undefined,
|
|
1104
|
-
})
|
|
1105
1014
|
}
|
|
1106
|
-
if (imageHelmOwner) {
|
|
1107
|
-
sources.push({
|
|
1108
|
-
type: 'Helm',
|
|
1109
|
-
label: `${imageHelmOwner.namespace}/${imageHelmOwner.name}`,
|
|
1110
|
-
onOpen: () => handleOpenHelmRelease(imageHelmOwner),
|
|
1111
|
-
})
|
|
1112
|
-
}
|
|
1113
|
-
return sources
|
|
1114
1015
|
}, [
|
|
1115
1016
|
handleOpenGitOpsResource,
|
|
1116
1017
|
handleOpenHelmRelease,
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
imageGitopsOwnerData,
|
|
1018
|
+
imageGuard,
|
|
1019
|
+
imageOwner,
|
|
1120
1020
|
imageHelmOwner,
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
inheritedGitOpsResponse.isPending,
|
|
1021
|
+
imageOwnerVerified,
|
|
1022
|
+
imageSelection,
|
|
1124
1023
|
])
|
|
1125
1024
|
const actionsBarProps = useMemo(
|
|
1126
1025
|
() => ({
|
|
@@ -1130,12 +1029,12 @@ export function WorkloadView({
|
|
|
1130
1029
|
: undefined,
|
|
1131
1030
|
onCompareTo,
|
|
1132
1031
|
onCompareAcrossClusters,
|
|
1133
|
-
|
|
1032
|
+
imageOwnership,
|
|
1134
1033
|
}),
|
|
1135
1034
|
[
|
|
1136
1035
|
baseActionsBarProps,
|
|
1137
1036
|
loadImagesWithTargetOwnership,
|
|
1138
|
-
|
|
1037
|
+
imageOwnership,
|
|
1139
1038
|
onCompareTo,
|
|
1140
1039
|
onCompareAcrossClusters,
|
|
1141
1040
|
],
|
|
@@ -1283,9 +1182,7 @@ export function WorkloadView({
|
|
|
1283
1182
|
onSelectRun={handleSelectedRunChange}
|
|
1284
1183
|
/>
|
|
1285
1184
|
)}
|
|
1286
|
-
|
|
1287
|
-
renderCNPGSummary({ apiKind: ak, namespace: ns, name: n, group: effectiveGroup, resource: res, context, onNavigate })
|
|
1288
|
-
}
|
|
1185
|
+
{...resourceDetailSlots({ kind: apiKind, group: resource?.apiVersion ? resourceGroup : effectiveGroup, namespace, name }, rest.extraTabs)}
|
|
1289
1186
|
renderExpandedOverview={({ kind: k, apiKind, namespace: ns, name: n, resource: res }) =>
|
|
1290
1187
|
supportsBatchExecution(k, apiKind, effectiveGroup, res?.apiVersion) &&
|
|
1291
1188
|
res ? (
|
|
@@ -1350,7 +1247,7 @@ export function WorkloadView({
|
|
|
1350
1247
|
onDuplicate={handleDuplicate}
|
|
1351
1248
|
onDownload={desktopDownload}
|
|
1352
1249
|
actionsBarProps={actionsBarProps}
|
|
1353
|
-
rendererOverrides={
|
|
1250
|
+
rendererOverrides={resourceRendererOverrides}
|
|
1354
1251
|
renderOverviewExtra={({ kind: k, namespace: ns, name: n, group: g, context }) => {
|
|
1355
1252
|
// Network entry kinds (Service/Ingress/Route/Gateway) ARE the diagnosis
|
|
1356
1253
|
// target: DiagnoseInlineSection renders in the drawer, no hint. Workload
|
|
@@ -1368,7 +1265,6 @@ export function WorkloadView({
|
|
|
1368
1265
|
<>
|
|
1369
1266
|
{diagnoseInline}
|
|
1370
1267
|
{diagnoseHint}
|
|
1371
|
-
<FluxSourceConsumersSection kind={k} namespace={ns} name={n} />
|
|
1372
1268
|
<AuditOverviewSection
|
|
1373
1269
|
findings={auditFindings ?? []}
|
|
1374
1270
|
onViewAll={() => navigateRouter('/checks')}
|
|
@@ -1379,6 +1275,7 @@ export function WorkloadView({
|
|
|
1379
1275
|
renderOverviewLead={() => (
|
|
1380
1276
|
<ResourceIssuesSection
|
|
1381
1277
|
issues={liveIssues}
|
|
1278
|
+
compact={!expanded}
|
|
1382
1279
|
subjectResource={{ kind: apiKind, namespace, name, group: rest.group }}
|
|
1383
1280
|
onResourceClick={
|
|
1384
1281
|
rest.onNavigateToResource
|
|
@@ -1395,7 +1292,7 @@ export function WorkloadView({
|
|
|
1395
1292
|
)}
|
|
1396
1293
|
hasOperationalIssues={hasOperationalIssues}
|
|
1397
1294
|
operationalIssuesPending={issuesPending}
|
|
1398
|
-
onOpenGitOpsResource={
|
|
1295
|
+
onOpenGitOpsResource={gitopsOwnerObject ? handleOpenGitOpsResource : undefined}
|
|
1399
1296
|
resolvedGitOpsOwner={gitopsOwner}
|
|
1400
1297
|
gitOpsOwnerVerified={gitOpsOwnerVerified}
|
|
1401
1298
|
gitOpsOwnerPending={gitOpsOwnerPending}
|
|
@@ -1405,6 +1302,7 @@ export function WorkloadView({
|
|
|
1405
1302
|
helmOwnerSource={helmOwnerSource}
|
|
1406
1303
|
onOpenHelmRelease={handleOpenHelmRelease}
|
|
1407
1304
|
onNavigateGitOpsPath={handleNavigateGitOpsPath}
|
|
1305
|
+
onOpenLiveTraffic={offerLiveTraffic ? openLiveTraffic : undefined}
|
|
1408
1306
|
/>
|
|
1409
1307
|
<CreateResourceDialog
|
|
1410
1308
|
open={duplicateDialogOpen}
|
|
@@ -1426,55 +1324,17 @@ export function WorkloadView({
|
|
|
1426
1324
|
)
|
|
1427
1325
|
}
|
|
1428
1326
|
|
|
1429
|
-
function collectServingRefs(relationships: Relationships | undefined): ResourceRef[] {
|
|
1327
|
+
export function collectServingRefs(relationships: Relationships | undefined): ResourceRef[] {
|
|
1430
1328
|
if (!relationships) return []
|
|
1431
|
-
return
|
|
1329
|
+
return dedupeResourceRefs([
|
|
1432
1330
|
...(relationships.services ?? []),
|
|
1433
1331
|
...(relationships.ingresses ?? []),
|
|
1434
1332
|
...(relationships.gateways ?? []),
|
|
1435
1333
|
...(relationships.routes ?? []),
|
|
1334
|
+
...(relationships.routedFrom ?? []),
|
|
1436
1335
|
])
|
|
1437
1336
|
}
|
|
1438
1337
|
|
|
1439
|
-
function dedupeRefs(refs: ResourceRef[]): ResourceRef[] {
|
|
1440
|
-
const seen = new Set<string>()
|
|
1441
|
-
return refs.filter((ref) => {
|
|
1442
|
-
const key = `${ref.kind}/${ref.namespace}/${ref.name}/${ref.group ?? ''}`
|
|
1443
|
-
if (seen.has(key)) return false
|
|
1444
|
-
seen.add(key)
|
|
1445
|
-
return true
|
|
1446
|
-
})
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
function resolveGitOpsOwner(
|
|
1450
|
-
owner: GitOpsOwnerRef | null,
|
|
1451
|
-
argoApplications: any[] | undefined,
|
|
1452
|
-
): GitOpsOwnerRef | null {
|
|
1453
|
-
if (!owner || owner.namespace || owner.tool !== 'argocd') return owner
|
|
1454
|
-
const matches = (argoApplications ?? []).filter((app) => app?.metadata?.name === owner.name)
|
|
1455
|
-
if (matches.length !== 1) return owner
|
|
1456
|
-
const namespace = matches[0]?.metadata?.namespace
|
|
1457
|
-
return namespace ? { ...owner, namespace } : owner
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
export function findInheritedGitOpsLookupRef(
|
|
1461
|
-
relationships: Relationships | undefined,
|
|
1462
|
-
directOwner: GitOpsOwnerRef | null,
|
|
1463
|
-
current: ResourceRef,
|
|
1464
|
-
): ResourceRef | null {
|
|
1465
|
-
if (directOwner) return null
|
|
1466
|
-
const inheritedManagerRefs = (relationships?.managedBy ?? []).filter(
|
|
1467
|
-
(ref) => !gitOpsOwnerFromRelationships({ managedBy: [ref] }) && !isNativeHelmManager(ref),
|
|
1468
|
-
)
|
|
1469
|
-
const candidates = [
|
|
1470
|
-
relationships?.deployment,
|
|
1471
|
-
...inheritedManagerRefs,
|
|
1472
|
-
relationships?.owner,
|
|
1473
|
-
].filter(Boolean) as ResourceRef[]
|
|
1474
|
-
|
|
1475
|
-
return candidates.find((ref) => !isCurrentResource(ref, current)) ?? null
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
1338
|
const POD_OWNERSHIP_WORKLOAD_KINDS = new Set([
|
|
1479
1339
|
'deployments',
|
|
1480
1340
|
'statefulsets',
|
|
@@ -1554,89 +1414,6 @@ function sameWorkload(
|
|
|
1554
1414
|
)
|
|
1555
1415
|
}
|
|
1556
1416
|
|
|
1557
|
-
function nativeHelmOwnerFromRelationships(
|
|
1558
|
-
relationships: Relationships | undefined,
|
|
1559
|
-
fallbackNamespace: string,
|
|
1560
|
-
): HelmOwnerRef | null {
|
|
1561
|
-
const ref = relationships?.managedBy?.[0]
|
|
1562
|
-
if (!ref || !isNativeHelmManager(ref)) return null
|
|
1563
|
-
return {
|
|
1564
|
-
namespace: ref.namespace || fallbackNamespace,
|
|
1565
|
-
name: ref.name,
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
function isCurrentResource(ref: ResourceRef, current: ResourceRef): boolean {
|
|
1570
|
-
return (
|
|
1571
|
-
kindToPluralWithGroup(ref.kind, ref.group ?? '') ===
|
|
1572
|
-
kindToPluralWithGroup(current.kind, current.group ?? '') &&
|
|
1573
|
-
ref.namespace === current.namespace &&
|
|
1574
|
-
ref.name === current.name &&
|
|
1575
|
-
(ref.group ?? '') === (current.group ?? '')
|
|
1576
|
-
)
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
function isNativeHelmManager(ref: ResourceRef): boolean {
|
|
1580
|
-
return ref.kind === 'HelmRelease' && ref.group !== 'helm.toolkit.fluxcd.io'
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
function describeGitOpsOwnerSource(owner: GitOpsOwnerRef | null, resource: any): string | null {
|
|
1584
|
-
if (!owner || !resource) return null
|
|
1585
|
-
const labels = resource.metadata?.labels ?? {}
|
|
1586
|
-
const annotations = resource.metadata?.annotations ?? {}
|
|
1587
|
-
|
|
1588
|
-
if (owner.tool === 'fluxcd') {
|
|
1589
|
-
const nameKey =
|
|
1590
|
-
owner.kind === 'helmreleases'
|
|
1591
|
-
? 'helm.toolkit.fluxcd.io/name'
|
|
1592
|
-
: 'kustomize.toolkit.fluxcd.io/name'
|
|
1593
|
-
const nsKey =
|
|
1594
|
-
owner.kind === 'helmreleases'
|
|
1595
|
-
? 'helm.toolkit.fluxcd.io/namespace'
|
|
1596
|
-
: 'kustomize.toolkit.fluxcd.io/namespace'
|
|
1597
|
-
if (labels[nameKey] || labels[nsKey]) {
|
|
1598
|
-
return `${nameKey}=${labels[nameKey] ?? ''}, ${nsKey}=${labels[nsKey] ?? ''}`
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
const trackingID = annotations['argocd.argoproj.io/tracking-id']
|
|
1603
|
-
if (trackingID) return `argocd.argoproj.io/tracking-id=${trackingID}`
|
|
1604
|
-
const argoInstance = labels['argocd.argoproj.io/instance']
|
|
1605
|
-
if (argoInstance) return `argocd.argoproj.io/instance=${argoInstance}`
|
|
1606
|
-
return null
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
function describeHelmOwnerSource(owner: HelmOwnerRef | null, resource: any): string | null {
|
|
1610
|
-
if (!owner || !resource) return null
|
|
1611
|
-
const annotations = resource.metadata?.annotations ?? {}
|
|
1612
|
-
const releaseName = annotations['meta.helm.sh/release-name']
|
|
1613
|
-
const releaseNamespace = annotations['meta.helm.sh/release-namespace']
|
|
1614
|
-
if (releaseName || releaseNamespace) {
|
|
1615
|
-
return `meta.helm.sh/release-name=${releaseName ?? ''}, meta.helm.sh/release-namespace=${releaseNamespace ?? ''}`
|
|
1616
|
-
}
|
|
1617
|
-
return null
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
function gitOpsOwnerGroup(owner: GitOpsOwnerRef): string {
|
|
1621
|
-
if (owner.tool === 'argocd') return 'argoproj.io'
|
|
1622
|
-
if (owner.kind === 'kustomizations') return 'kustomize.toolkit.fluxcd.io'
|
|
1623
|
-
return 'helm.toolkit.fluxcd.io'
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
function deriveGitOpsOwnerStatus(owner: GitOpsOwnerRef | null, resource: any): GitOpsStatus | null {
|
|
1627
|
-
if (!owner || !resource || !hasGitOpsStatusPayload(owner, resource)) return null
|
|
1628
|
-
return getGitOpsResourceStatus(owner.kind, resource)
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
function hasGitOpsStatusPayload(owner: GitOpsOwnerRef, resource: any): boolean {
|
|
1632
|
-
if (owner.kind === 'applications') {
|
|
1633
|
-
const status = resource.status ?? {}
|
|
1634
|
-
return Boolean(status.sync?.status || status.health?.status || status.operationState?.phase)
|
|
1635
|
-
}
|
|
1636
|
-
if (resource.spec?.suspend === true) return true
|
|
1637
|
-
return Array.isArray(resource.status?.conditions) && resource.status.conditions.length > 0
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
1417
|
// ============================================================================
|
|
1641
1418
|
// LOGS TAB — platform-specific (uses data-fetching hooks)
|
|
1642
1419
|
// ============================================================================
|
|
@@ -1694,9 +1471,8 @@ function LogsTabContent({
|
|
|
1694
1471
|
)
|
|
1695
1472
|
}
|
|
1696
1473
|
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
}
|
|
1474
|
+
const integrationLogs = resourceLogs({ kind, group, namespace, name }, resource)
|
|
1475
|
+
if (integrationLogs) return integrationLogs
|
|
1700
1476
|
|
|
1701
1477
|
// Workload kinds with stable pod selectors use the aggregated workload logs viewer
|
|
1702
1478
|
if (WORKLOAD_LOG_KINDS.has(kind) && (kind !== 'Job' || isCoreBatchJob(apiKind, group))) {
|
|
@@ -2372,137 +2148,6 @@ function DiagnoseInlineSection({ kind, namespace, name, group, onOpenReachabilit
|
|
|
2372
2148
|
)
|
|
2373
2149
|
}
|
|
2374
2150
|
|
|
2375
|
-
// FluxSourceConsumersSection lists the reconcilers (Kustomization, HelmRelease)
|
|
2376
|
-
// that reference this Flux source CR — the inverse of `spec.sourceRef`. Renders
|
|
2377
|
-
// only when the focused resource is a Flux source kind; otherwise null. Sources
|
|
2378
|
-
// can have many consumers (one repo feeding multiple apps), so this answers
|
|
2379
|
-
// "if I edit this source, what gets affected on the next reconcile?".
|
|
2380
|
-
//
|
|
2381
|
-
// Filtering happens client-side off the namespaced reconciler lists — these
|
|
2382
|
-
// are typically small (tens, not thousands) and the dynamic informer cache
|
|
2383
|
-
// makes the request cheap. If a cluster ever has thousands of HelmReleases,
|
|
2384
|
-
// a dedicated /api/gitops/consumers endpoint would be the right move; today
|
|
2385
|
-
// it'd be premature.
|
|
2386
|
-
// Outer component is cheap — it does only the kind check and decides whether
|
|
2387
|
-
// to mount the data-fetching child. Without this split, useResources would
|
|
2388
|
-
// fire two API calls on EVERY workload drawer open (Pod, Deployment, Service,
|
|
2389
|
-
// …), since the hook has no `enabled` flag and can't be conditionally called
|
|
2390
|
-
// (Rules of Hooks). The hooks only need to run when the focused resource is
|
|
2391
|
-
// actually a Flux source CR.
|
|
2392
|
-
function FluxSourceConsumersSection({
|
|
2393
|
-
kind,
|
|
2394
|
-
namespace,
|
|
2395
|
-
name,
|
|
2396
|
-
}: {
|
|
2397
|
-
kind: string
|
|
2398
|
-
namespace: string
|
|
2399
|
-
name: string
|
|
2400
|
-
}) {
|
|
2401
|
-
// The inner WorkloadView de-pluralizes the URL's plural form, which gives
|
|
2402
|
-
// "Gitrepository" (single-uppercase) rather than the wire-correct
|
|
2403
|
-
// "GitRepository" — so we match lowercase. spec.sourceRef.kind on consumers
|
|
2404
|
-
// is always wire-correct, so we look that up separately.
|
|
2405
|
-
const sourceKind = FLUX_SOURCE_KIND_BY_LOWER.get(kind.toLowerCase()) ?? null
|
|
2406
|
-
if (!sourceKind) return null
|
|
2407
|
-
return <FluxSourceConsumersInner sourceKind={sourceKind} namespace={namespace} name={name} />
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
|
-
function FluxSourceConsumersInner({
|
|
2411
|
-
sourceKind,
|
|
2412
|
-
namespace,
|
|
2413
|
-
name,
|
|
2414
|
-
}: {
|
|
2415
|
-
sourceKind: string
|
|
2416
|
-
namespace: string
|
|
2417
|
-
name: string
|
|
2418
|
-
}) {
|
|
2419
|
-
const navigate = useNavigate()
|
|
2420
|
-
const { data: kustomizations } = useResources<any>(
|
|
2421
|
-
'kustomizations',
|
|
2422
|
-
undefined,
|
|
2423
|
-
'kustomize.toolkit.fluxcd.io',
|
|
2424
|
-
)
|
|
2425
|
-
const { data: helmReleases } = useResources<any>(
|
|
2426
|
-
'helmreleases',
|
|
2427
|
-
undefined,
|
|
2428
|
-
'helm.toolkit.fluxcd.io',
|
|
2429
|
-
)
|
|
2430
|
-
|
|
2431
|
-
const consumers: Array<{
|
|
2432
|
-
kind: 'Kustomization' | 'HelmRelease'
|
|
2433
|
-
namespace: string
|
|
2434
|
-
name: string
|
|
2435
|
-
plural: string
|
|
2436
|
-
}> = []
|
|
2437
|
-
for (const k of kustomizations ?? []) {
|
|
2438
|
-
const ref = k?.spec?.sourceRef ?? {}
|
|
2439
|
-
const refNs = ref.namespace || k?.metadata?.namespace
|
|
2440
|
-
if (ref.kind === sourceKind && ref.name === name && refNs === namespace) {
|
|
2441
|
-
consumers.push({
|
|
2442
|
-
kind: 'Kustomization',
|
|
2443
|
-
namespace: k.metadata.namespace,
|
|
2444
|
-
name: k.metadata.name,
|
|
2445
|
-
plural: 'kustomizations',
|
|
2446
|
-
})
|
|
2447
|
-
}
|
|
2448
|
-
}
|
|
2449
|
-
for (const h of helmReleases ?? []) {
|
|
2450
|
-
const ref = h?.spec?.chart?.spec?.sourceRef ?? {}
|
|
2451
|
-
const refNs = ref.namespace || h?.metadata?.namespace
|
|
2452
|
-
if (ref.kind === sourceKind && ref.name === name && refNs === namespace) {
|
|
2453
|
-
consumers.push({
|
|
2454
|
-
kind: 'HelmRelease',
|
|
2455
|
-
namespace: h.metadata.namespace,
|
|
2456
|
-
name: h.metadata.name,
|
|
2457
|
-
plural: 'helmreleases',
|
|
2458
|
-
})
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
|
|
2462
|
-
if (consumers.length === 0) {
|
|
2463
|
-
return (
|
|
2464
|
-
<section className="rounded-lg border border-theme-border bg-theme-surface p-4 shadow-theme-sm">
|
|
2465
|
-
<h3 className="mb-2 text-sm font-semibold text-theme-text-primary">Consumed by</h3>
|
|
2466
|
-
<p className="text-xs text-theme-text-tertiary">
|
|
2467
|
-
No Kustomization or HelmRelease references this source.
|
|
2468
|
-
</p>
|
|
2469
|
-
</section>
|
|
2470
|
-
)
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2473
|
-
return (
|
|
2474
|
-
<section className="rounded-lg border border-theme-border bg-theme-surface p-4 shadow-theme-sm">
|
|
2475
|
-
<h3 className="mb-3 text-sm font-semibold text-theme-text-primary">
|
|
2476
|
-
Consumed by ({consumers.length})
|
|
2477
|
-
</h3>
|
|
2478
|
-
<div className="flex flex-wrap gap-1.5">
|
|
2479
|
-
{consumers.map((c) => (
|
|
2480
|
-
<Tooltip
|
|
2481
|
-
key={`${c.kind}/${c.namespace}/${c.name}`}
|
|
2482
|
-
content={`${c.kind} ${c.namespace}/${c.name}`}
|
|
2483
|
-
>
|
|
2484
|
-
<button
|
|
2485
|
-
onClick={() =>
|
|
2486
|
-
navigate(
|
|
2487
|
-
`/gitops/detail/${c.plural}/${encodeURIComponent(c.namespace)}/${encodeURIComponent(c.name)}`,
|
|
2488
|
-
)
|
|
2489
|
-
}
|
|
2490
|
-
className="inline-flex items-center gap-1.5 rounded border border-theme-border bg-theme-surface px-1.5 py-0.5 text-[11px] text-theme-text-secondary hover:border-skyhook-500/60 hover:text-skyhook-500 transition-colors"
|
|
2491
|
-
>
|
|
2492
|
-
<span className="text-theme-text-tertiary">
|
|
2493
|
-
{c.kind === 'HelmRelease' ? 'HR' : 'K'}
|
|
2494
|
-
</span>
|
|
2495
|
-
<span>
|
|
2496
|
-
{c.namespace}/{c.name}
|
|
2497
|
-
</span>
|
|
2498
|
-
</button>
|
|
2499
|
-
</Tooltip>
|
|
2500
|
-
))}
|
|
2501
|
-
</div>
|
|
2502
|
-
</section>
|
|
2503
|
-
)
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
2151
|
// Drawer mode: single chart + category tabs (compact for ~500px width).
|
|
2507
2152
|
// Full-screen mode: multi-chart grid so CPU + Memory + Network can be
|
|
2508
2153
|
// compared side-by-side without tab switching.
|
|
@@ -2577,18 +2222,6 @@ function DrawerMetricsContent({
|
|
|
2577
2222
|
)
|
|
2578
2223
|
}
|
|
2579
2224
|
|
|
2580
|
-
// FLUX_SOURCE_KIND_BY_LOWER maps lowercase kind (what the inner WorkloadView
|
|
2581
|
-
// produces via its plural-to-singular fallback) to the wire-correct
|
|
2582
|
-
// PascalCase form that consumers carry in spec.sourceRef.kind. HelmChart is
|
|
2583
|
-
// intentionally absent — it's an auto-generated internal CR, not something
|
|
2584
|
-
// users create or point reconcilers at directly.
|
|
2585
|
-
const FLUX_SOURCE_KIND_BY_LOWER = new Map<string, string>([
|
|
2586
|
-
['gitrepository', 'GitRepository'],
|
|
2587
|
-
['helmrepository', 'HelmRepository'],
|
|
2588
|
-
['ocirepository', 'OCIRepository'],
|
|
2589
|
-
['bucket', 'Bucket'],
|
|
2590
|
-
])
|
|
2591
|
-
|
|
2592
2225
|
// Read-only manifest view for an object in the workload's neighborhood (the
|
|
2593
2226
|
// YAML tab's object rail). Read-only by design — editing an arbitrary related
|
|
2594
2227
|
// object belongs on that resource's own page.
|