@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,204 +1,181 @@
|
|
|
1
|
-
import { useState,
|
|
1
|
+
import { useId, useState, type FormEvent } from 'react'
|
|
2
2
|
import { DialogPortal } from '@skyhook-io/k8s-ui/components/ui/DialogPortal'
|
|
3
|
-
import {
|
|
4
|
-
import { clsx } from 'clsx'
|
|
5
|
-
import { useHelmOCISources, useAddOCISource, useRemoveOCISource, useClusterInfo, useCapabilities, useCloudRole } from '../../api/client'
|
|
6
|
-
import { OperatorManagedNotice } from '../settings/OperatorManagedNotice'
|
|
3
|
+
import { AlertTriangle, Link2, Plus, Trash2, X } from 'lucide-react'
|
|
7
4
|
import { Input } from '@skyhook-io/k8s-ui'
|
|
8
|
-
import
|
|
5
|
+
import { useAddHelmRepository, useAddOCISource, useCapabilities, useCloudRole, useClusterInfo, useHelmOCISources, useHelmRepositories, useHelmSourceStatus, useRemoveOCISource, useSetHelmSource } from '../../api/client'
|
|
6
|
+
import { OperatorManagedNotice } from '../settings/OperatorManagedNotice'
|
|
7
|
+
import type { ChartSourceStatus, HelmRepository, UpgradeInfo } from '../../types'
|
|
9
8
|
|
|
10
9
|
interface TrackChartSourceDialogProps {
|
|
11
10
|
open: boolean
|
|
12
11
|
onClose: () => void
|
|
13
|
-
/** Chart name of the release this was opened from, for the example prompt. */
|
|
14
12
|
chartName?: string
|
|
13
|
+
namespace: string
|
|
14
|
+
releaseName: string
|
|
15
15
|
sourceIssue?: UpgradeInfo['sourceIssue']
|
|
16
16
|
sourceError?: string
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function
|
|
19
|
+
export function isValidHelmRepositoryURL(value: string) {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = new URL(value.trim())
|
|
22
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:'
|
|
23
|
+
} catch {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sourceStateLabel(recorded: boolean, configured: boolean, available: boolean) {
|
|
29
|
+
if (!recorded) return 'Not recorded'
|
|
30
|
+
if (!configured) return 'Recorded · not configured here'
|
|
31
|
+
if (!available) return 'Recorded and configured · unavailable or version missing'
|
|
32
|
+
return 'Recorded · configured · available'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// What the dialog says about the release's recorded source. "Not recorded" is
|
|
36
|
+
// a claim about the release, so it shows only once the read has succeeded.
|
|
37
|
+
export function sourceStatusLine(status: ChartSourceStatus | undefined, error: unknown) {
|
|
38
|
+
if (status) return sourceStateLabel(Boolean(status.recorded), status.configured, status.available)
|
|
39
|
+
if (error) return `Could not read this release's source: ${error instanceof Error ? error.message : String(error)}`
|
|
40
|
+
return 'Checking the recorded source...'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Short form of a repository URL for a button label: host and path, no scheme.
|
|
44
|
+
export function repositoryDisplayURL(url: string) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = new URL(url)
|
|
47
|
+
return `${parsed.host}${parsed.pathname}`.replace(/\/+$/, '')
|
|
48
|
+
} catch {
|
|
49
|
+
return url
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Mirrors normalizedRepositoryName in internal/helm/chart_sources.go. The
|
|
54
|
+
// server stores names in this form, so collisions are checked on it.
|
|
55
|
+
const MAX_REPOSITORY_NAME_LENGTH = 40
|
|
56
|
+
export function normalizedRepositoryName(name: string) {
|
|
57
|
+
const base = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^[-._]+|[-._]+$/g, '')
|
|
58
|
+
return base.length > MAX_REPOSITORY_NAME_LENGTH ? base.slice(0, MAX_REPOSITORY_NAME_LENGTH).replace(/[-._]+$/, '') : base
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Name for re-adding the recorded repository on this machine. Reuses the
|
|
62
|
+
// recorded alias when it is free here; otherwise the first free "<alias>-N",
|
|
63
|
+
// so an alias that points at a different repository is never reused. The
|
|
64
|
+
// server refuses a taken name, which covers a repository added meanwhile.
|
|
65
|
+
// An empty result lets the server pick a name from the URL.
|
|
66
|
+
export function recordedRepositoryName(alias: string, repositories: HelmRepository[]) {
|
|
67
|
+
const base = normalizedRepositoryName(alias)
|
|
68
|
+
if (!base) return ''
|
|
69
|
+
const taken = new Set(repositories.map(repository => normalizedRepositoryName(repository.name)))
|
|
70
|
+
if (!taken.has(base)) return base
|
|
71
|
+
for (let n = 2; ; n++) {
|
|
72
|
+
const suffix = `-${n}`
|
|
73
|
+
const name = base.slice(0, MAX_REPOSITORY_NAME_LENGTH - suffix.length).replace(/[-._]+$/, '') + suffix
|
|
74
|
+
if (!taken.has(name)) return name
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getSourceIssueCopy(sourceIssue: UpgradeInfo['sourceIssue'], sourceError?: string) {
|
|
20
79
|
switch (sourceIssue) {
|
|
21
|
-
case 'repo_index_error':
|
|
22
|
-
return {
|
|
23
|
-
title: 'A Helm repo index failed',
|
|
24
|
-
body: operatorManaged
|
|
25
|
-
? 'Ask the operator to fix the Helm repo index, or add the chart’s OCI registry prefix to helm.ociSources if it came from OCI.'
|
|
26
|
-
: 'Fix, refresh, or remove the broken Helm repo index. If this release came from OCI, add its registry prefix and Radar will check OCI before reporting that repo error.',
|
|
27
|
-
}
|
|
80
|
+
case 'repo_index_error': return { title: 'A Helm repo index failed', body: 'Fix or refresh the repository before associating it.' }
|
|
28
81
|
case 'ambiguous_repository':
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
case 'untracked':
|
|
34
|
-
return {
|
|
35
|
-
title: 'Source not tracked',
|
|
36
|
-
body: operatorManaged
|
|
37
|
-
? 'Helm does not record the install source. Ask the operator to add the OCI prefix that contains this chart to helm.ociSources.'
|
|
38
|
-
: 'Helm does not record the install source. Add the OCI prefix that contains this chart.',
|
|
39
|
-
}
|
|
40
|
-
default:
|
|
41
|
-
return sourceError
|
|
42
|
-
? { title: 'Source unresolved', body: sourceError }
|
|
43
|
-
: undefined
|
|
82
|
+
case 'ambiguous_source': return { title: 'Multiple chart sources match', body: 'Radar will not guess. Select the source that originally supplied this release.' }
|
|
83
|
+
case 'source_unavailable': return { title: 'Recorded source unavailable', body: 'The release provenance is preserved, but that source cannot currently be verified here.' }
|
|
84
|
+
case 'untracked': return { title: 'Source not recorded', body: 'Select an exact configured match, or add the original classic repository or OCI prefix.' }
|
|
85
|
+
default: return sourceError ? { title: 'Source unresolved', body: sourceError } : undefined
|
|
44
86
|
}
|
|
45
87
|
}
|
|
46
88
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const titleId = useId()
|
|
54
|
-
const [value, setValue] = useState('')
|
|
55
|
-
const { data: sources } = useHelmOCISources()
|
|
89
|
+
export function TrackChartSourceDialog({ open, onClose, chartName, namespace, releaseName, sourceIssue, sourceError }: TrackChartSourceDialogProps) {
|
|
90
|
+
const [ociPrefix, setOCIPrefix] = useState('')
|
|
91
|
+
const [repositoryName, setRepositoryName] = useState('')
|
|
92
|
+
const [repositoryURL, setRepositoryURL] = useState('')
|
|
93
|
+
const { data: status, error: statusError, refetch: refetchStatus } = useHelmSourceStatus(namespace, releaseName, open)
|
|
94
|
+
const { data: ociSources } = useHelmOCISources()
|
|
56
95
|
const { data: clusterInfo } = useClusterInfo()
|
|
57
|
-
const addSource = useAddOCISource()
|
|
58
|
-
const removeSource = useRemoveOCISource()
|
|
59
96
|
const { data: capabilities } = useCapabilities()
|
|
60
|
-
const
|
|
97
|
+
const addOCI = useAddOCISource()
|
|
98
|
+
const removeOCI = useRemoveOCISource()
|
|
99
|
+
const addRepository = useAddHelmRepository()
|
|
100
|
+
const setSource = useSetHelmSource(namespace, releaseName)
|
|
101
|
+
const issueCopy = getSourceIssueCopy(sourceIssue, sourceError)
|
|
102
|
+
const recorded = status?.recorded
|
|
103
|
+
const configManaged = capabilities?.configManagement === 'operator'
|
|
61
104
|
// Chart sources are shared configuration that every user's upgrades resolve
|
|
62
105
|
// against, so in Radar Cloud only owners change them (the server enforces it).
|
|
63
106
|
const ownerOnly = !useCloudRole().canAtLeast('owner')
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
const trimmed = value.trim()
|
|
72
|
-
const invalid = trimmed !== '' && !trimmed.startsWith('oci://')
|
|
73
|
-
const normalizedInput = trimmed.replace(/\/+$/, '')
|
|
74
|
-
const normalizedChartName = chartName?.trim().replace(/^\/+|\/+$/g, '') ?? ''
|
|
75
|
-
const probedRef = normalizedInput && normalizedChartName ? `${normalizedInput}/${normalizedChartName}` : ''
|
|
76
|
-
const looksLikeFullChartRef = Boolean(normalizedInput && normalizedChartName && normalizedInput.endsWith(`/${normalizedChartName}`))
|
|
77
|
-
const sourceIssueCopy = getSourceIssueCopy(sourceIssue, sourceError, operatorManaged)
|
|
107
|
+
const canEditSources = capabilities != null && !configManaged && !ownerOnly
|
|
108
|
+
const titleId = useId()
|
|
109
|
+
// The release says which repository it came from, but this machine has no
|
|
110
|
+
// repository with that URL: offer to add exactly that one.
|
|
111
|
+
const missingRecordedRepository = status && !status.configured && recorded?.type === 'repository' && recorded.url ? recorded : undefined
|
|
112
|
+
const { data: repositories, isLoading: repositoriesLoading } = useHelmRepositories()
|
|
113
|
+
const recordedName = missingRecordedRepository ? recordedRepositoryName(missingRecordedRepository.reference, repositories ?? []) : undefined
|
|
78
114
|
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
81
|
-
|
|
115
|
+
const addRecordedRepository = () => {
|
|
116
|
+
if (!missingRecordedRepository || recordedName === undefined) return
|
|
117
|
+
addRepository.mutate({ name: recordedName, url: missingRecordedRepository.url, namespace, releaseName }, { onSuccess: onClose })
|
|
82
118
|
}
|
|
83
119
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
<h3 id={titleId} className="text-lg font-semibold text-theme-text-primary">Track chart source</h3>
|
|
92
|
-
<p className="text-sm text-theme-text-secondary mt-1">
|
|
93
|
-
Helm doesn't record where a chart was installed from. Radar checks registered OCI
|
|
94
|
-
prefixes for newer versions of your charts.
|
|
95
|
-
</p>
|
|
96
|
-
</div>
|
|
97
|
-
<button
|
|
98
|
-
onClick={onClose}
|
|
99
|
-
aria-label="Close"
|
|
100
|
-
className="p-1 text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded"
|
|
101
|
-
>
|
|
102
|
-
<X className="w-5 h-5" />
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<div className="p-4 space-y-4">
|
|
107
|
-
{operatorManaged && <OperatorManagedNotice helmValue="helm.ociSources" />}
|
|
108
|
-
{!operatorManaged && ownerOnly && (
|
|
109
|
-
<p className="text-sm text-theme-text-secondary">
|
|
110
|
-
Only organization owners can add or remove chart sources.
|
|
111
|
-
</p>
|
|
112
|
-
)}
|
|
113
|
-
{sourceIssueCopy && (
|
|
114
|
-
<div className="flex items-start gap-2 rounded-lg border border-theme-border bg-theme-elevated px-3 py-2 text-sm">
|
|
115
|
-
<AlertTriangle className="w-4 h-4 shrink-0 mt-0.5 text-theme-text-secondary" />
|
|
116
|
-
<div>
|
|
117
|
-
<p className="font-medium text-theme-text-primary">{sourceIssueCopy.title}</p>
|
|
118
|
-
<p className="text-theme-text-secondary">{sourceIssueCopy.body}</p>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
)}
|
|
122
|
-
|
|
123
|
-
{canEdit && <div>
|
|
124
|
-
<label className="block text-sm font-medium text-theme-text-secondary mb-2">
|
|
125
|
-
OCI registry prefix
|
|
126
|
-
</label>
|
|
127
|
-
<div className="flex gap-2">
|
|
128
|
-
<Input
|
|
129
|
-
value={value}
|
|
130
|
-
onChange={(e) => setValue(e.target.value)}
|
|
131
|
-
onKeyDown={(e) => e.key === 'Enter' && handleAdd()}
|
|
132
|
-
placeholder="oci://ghcr.io/myorg/charts"
|
|
133
|
-
aria-invalid={invalid ? true : undefined}
|
|
134
|
-
className={clsx(
|
|
135
|
-
'flex-1 px-3 py-2 bg-theme-elevated border rounded-lg text-sm text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-2',
|
|
136
|
-
invalid ? 'border-red-500/60 focus:ring-red-500' : 'border-theme-border-light focus:ring-accent',
|
|
137
|
-
)}
|
|
138
|
-
/>
|
|
139
|
-
<button
|
|
140
|
-
onClick={handleAdd}
|
|
141
|
-
disabled={!trimmed || invalid || addSource.isPending}
|
|
142
|
-
className="btn-brand px-3 py-2 text-sm inline-flex items-center gap-1 disabled:opacity-50 disabled:pointer-events-none"
|
|
143
|
-
>
|
|
144
|
-
<Plus className="w-4 h-4" />
|
|
145
|
-
Add
|
|
146
|
-
</button>
|
|
147
|
-
</div>
|
|
148
|
-
<p className="mt-1 text-xs text-theme-text-tertiary">
|
|
149
|
-
{invalid
|
|
150
|
-
? 'Must be an oci:// reference.'
|
|
151
|
-
: looksLikeFullChartRef
|
|
152
|
-
? `This looks like a full chart ref. Radar would probe "${probedRef}"; remove the final "/${normalizedChartName}" if "${normalizedChartName}" is the chart name.`
|
|
153
|
-
: chartName
|
|
154
|
-
? `Radar will probe "${probedRef || `<prefix>/${normalizedChartName}`}".`
|
|
155
|
-
: 'Radar probes <prefix>/<chartName> for each untracked release.'}
|
|
156
|
-
</p>
|
|
157
|
-
</div>}
|
|
120
|
+
const addRepositoryAndAssociate = (event: FormEvent) => {
|
|
121
|
+
event.preventDefault()
|
|
122
|
+
const name = repositoryName.trim()
|
|
123
|
+
const url = repositoryURL.trim()
|
|
124
|
+
if (!name || !isValidHelmRepositoryURL(url)) return
|
|
125
|
+
addRepository.mutate({ name, url, namespace, releaseName }, { onSuccess: onClose })
|
|
126
|
+
}
|
|
158
127
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<li
|
|
167
|
-
key={src}
|
|
168
|
-
className="flex items-center justify-between gap-2 px-3 py-2 bg-theme-elevated rounded-lg"
|
|
169
|
-
>
|
|
170
|
-
<span className="text-sm text-theme-text-primary font-mono truncate">{src}</span>
|
|
171
|
-
{canEdit && <button
|
|
172
|
-
onClick={() => removeSource.mutate(src)}
|
|
173
|
-
disabled={removeSource.isPending}
|
|
174
|
-
className="p-1 text-theme-text-secondary hover:text-red-400 hover:bg-red-500/10 rounded disabled:opacity-50"
|
|
175
|
-
aria-label={`Remove ${src}`}
|
|
176
|
-
>
|
|
177
|
-
<Trash2 className="w-4 h-4" />
|
|
178
|
-
</button>}
|
|
179
|
-
</li>
|
|
180
|
-
))}
|
|
181
|
-
</ul>
|
|
182
|
-
</div>
|
|
183
|
-
)}
|
|
128
|
+
const addOCIPrefix = async (event: FormEvent) => {
|
|
129
|
+
event.preventDefault()
|
|
130
|
+
if (!ociPrefix.trim().startsWith('oci://')) return
|
|
131
|
+
await addOCI.mutateAsync(ociPrefix.trim())
|
|
132
|
+
setOCIPrefix('')
|
|
133
|
+
await refetchStatus()
|
|
134
|
+
}
|
|
184
135
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
)}
|
|
136
|
+
return <DialogPortal ariaLabelledBy={titleId} open={open} onClose={onClose} className="max-w-2xl w-full">
|
|
137
|
+
<div className="flex items-start gap-3 p-4 border-b border-theme-border">
|
|
138
|
+
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-theme-hover"><Link2 className="w-5 h-5" /></div>
|
|
139
|
+
<div className="flex-1 min-w-0"><h3 id={titleId} className="text-lg font-semibold">Track chart source</h3><p className="text-sm text-theme-text-secondary mt-1">Tell Radar which repository or OCI source this release was installed from. Upgrades then come only from that source.</p></div>
|
|
140
|
+
<button onClick={onClose} aria-label="Close" className="p-1 rounded hover:bg-theme-elevated"><X className="w-5 h-5" /></button>
|
|
141
|
+
</div>
|
|
142
|
+
<div className="p-4 space-y-4">
|
|
143
|
+
{configManaged && <OperatorManagedNotice helmValue="helm.ociSources" />}
|
|
144
|
+
{!configManaged && ownerOnly && <p className="text-sm text-theme-text-secondary">Only organization owners can add or remove chart sources.</p>}
|
|
145
|
+
{issueCopy && <div className="flex gap-2 rounded-lg border border-theme-border bg-theme-elevated px-3 py-2 text-sm"><AlertTriangle className="w-4 h-4 mt-0.5 shrink-0" /><div><p className="font-medium">{issueCopy.title}</p><p className="text-theme-text-secondary">{issueCopy.body}</p></div></div>}
|
|
146
|
+
<div className="rounded-lg border border-theme-border px-3 py-2">
|
|
147
|
+
<p className="text-xs uppercase tracking-wide text-theme-text-tertiary">Release source state</p>
|
|
148
|
+
<p className="text-sm font-medium mt-1 break-all">{sourceStatusLine(status, statusError)}</p>
|
|
149
|
+
{!status && statusError && <button type="button" onClick={() => refetchStatus()} className="mt-2 px-3 py-1.5 text-sm text-theme-text-primary border border-theme-border rounded-lg hover:bg-theme-elevated transition-colors">Retry</button>}
|
|
150
|
+
{recorded && <p className="font-mono text-xs text-theme-text-secondary mt-1 break-all">{recorded.url ?? recorded.reference}</p>}
|
|
201
151
|
</div>
|
|
202
|
-
|
|
203
|
-
|
|
152
|
+
{missingRecordedRepository && recordedName !== undefined && <div className="space-y-1">
|
|
153
|
+
<button type="button" onClick={addRecordedRepository} disabled={!canEditSources || repositoriesLoading || addRepository.isPending} className="btn-brand inline-flex items-center gap-1 px-3 py-2 max-w-full"><Plus className="w-4 h-4 shrink-0" /><span className="truncate">Add {repositoryDisplayURL(missingRecordedRepository.url)} and use it</span></button>
|
|
154
|
+
<p className="text-xs text-theme-text-tertiary">{!recordedName
|
|
155
|
+
? 'Radar picks a name for it in Helm.'
|
|
156
|
+
: recordedName === missingRecordedRepository.reference
|
|
157
|
+
? <>Saved in Helm as <span className="font-mono">{recordedName}</span>, the name it was recorded under.</>
|
|
158
|
+
: recordedName === normalizedRepositoryName(missingRecordedRepository.reference)
|
|
159
|
+
? <>Saved in Helm as <span className="font-mono">{recordedName}</span>.</>
|
|
160
|
+
: <>Saved in Helm as <span className="font-mono">{recordedName}</span>. The name <span className="font-mono">{missingRecordedRepository.reference}</span> already points to a different repository here.</>}</p>
|
|
161
|
+
</div>}
|
|
162
|
+
{status?.candidates && status.candidates.length > 0 && <div>
|
|
163
|
+
<p className="text-xs uppercase tracking-wide text-theme-text-tertiary mb-2">Configured sources that publish {chartName || 'this chart'} at the installed version</p>
|
|
164
|
+
<div className="space-y-2">{status.candidates.map(candidate => <button key={`${candidate.type}:${candidate.url ?? candidate.reference}`} onClick={() => setSource.mutate(candidate, { onSuccess: onClose })} disabled={setSource.isPending} className="w-full text-left rounded-lg border border-theme-border px-3 py-2 hover:border-accent disabled:opacity-50"><span className="text-xs uppercase text-theme-text-tertiary">{candidate.type}</span><span className="block font-mono text-sm break-all">{candidate.url ?? candidate.reference}</span></button>)}</div>
|
|
165
|
+
{status.candidates.length > 1 && <p className="text-xs text-amber-600 mt-2">Multiple sources publish this exact chart and version. Choose explicitly; Radar will not auto-associate one.</p>}
|
|
166
|
+
</div>}
|
|
167
|
+
<form onSubmit={addRepositoryAndAssociate} className="space-y-2">
|
|
168
|
+
<p className="text-xs uppercase tracking-wide text-theme-text-tertiary">Add classic repository and associate</p>
|
|
169
|
+
<div className="grid grid-cols-1 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.8fr)_auto] gap-2"><Input value={repositoryName} onChange={event => setRepositoryName(event.target.value)} placeholder="Repository name" disabled={!canEditSources} /><Input value={repositoryURL} onChange={event => setRepositoryURL(event.target.value)} placeholder="https://charts.example.com" disabled={!canEditSources} /><button type="submit" disabled={!canEditSources || !repositoryName.trim() || !isValidHelmRepositoryURL(repositoryURL) || addRepository.isPending} className="btn-brand inline-flex items-center justify-center gap-1 px-3 py-2"><Plus className="w-4 h-4" /> Add</button></div>
|
|
170
|
+
<p className="text-xs text-theme-text-tertiary">Association succeeds only if the repository publishes {chartName || 'the installed chart'} at the exact installed version.</p>
|
|
171
|
+
</form>
|
|
172
|
+
<form onSubmit={addOCIPrefix} className="space-y-2">
|
|
173
|
+
<p className="text-xs uppercase tracking-wide text-theme-text-tertiary">Register OCI prefix</p>
|
|
174
|
+
<div className="flex gap-2"><Input value={ociPrefix} onChange={event => setOCIPrefix(event.target.value)} placeholder="oci://registry.example.com/charts" disabled={!canEditSources} /><button type="submit" disabled={!canEditSources || !ociPrefix.trim().startsWith('oci://') || addOCI.isPending} className="btn-brand px-3 py-2">Add</button></div>
|
|
175
|
+
<p className="text-xs text-theme-text-tertiary">After registration, select it in the list above. Credentials are reused from Helm and are never recorded in release provenance.</p>
|
|
176
|
+
</form>
|
|
177
|
+
{ociSources && ociSources.length > 0 && <ul className="space-y-1">{ociSources.map(source => <li key={source} className="flex items-center justify-between gap-2 rounded-lg bg-theme-elevated px-3 py-2"><span className="font-mono text-sm truncate">{source}</span>{canEditSources && <button onClick={() => removeOCI.mutate(source)} disabled={removeOCI.isPending} aria-label={`Remove ${source}`} className="p-1 hover:text-red-400 disabled:opacity-50"><Trash2 className="w-4 h-4" /></button>}</li>)}</ul>}
|
|
178
|
+
{clusterInfo?.inCluster && <p className="text-xs text-amber-600 flex items-start gap-2"><AlertTriangle className="w-4 h-4 shrink-0" />Private OCI sources require credentials available to the in-cluster Radar process.</p>}
|
|
179
|
+
</div>
|
|
180
|
+
</DialogPortal>
|
|
204
181
|
}
|