@skyhook-io/radar-app 1.13.5 → 1.14.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/package.json +9 -9
- package/src/App.tsx +66 -28
- package/src/api/client.authRedirect.test.ts +12 -1
- package/src/api/client.prometheusStatus.test.ts +24 -0
- package/src/api/client.ts +324 -16
- package/src/api/diagnose.ts +32 -1
- package/src/api/drain-plan.test.ts +86 -0
- package/src/api/quotas.test.ts +26 -0
- package/src/api/quotas.ts +20 -0
- package/src/api/workloadMetrics.test.ts +40 -0
- package/src/api/workloadMetrics.ts +68 -0
- package/src/components/CloudConnectFlow.tsx +343 -46
- package/src/components/CloudFunnelButton.tsx +110 -37
- package/src/components/ConnectionErrorView.tsx +9 -10
- package/src/components/DebugOverlay.tsx +10 -6
- package/src/components/SyncProgressPanel.tsx +73 -0
- package/src/components/audit/AuditView.tsx +2 -0
- package/src/components/cloudConnectHandoff.test.ts +245 -0
- package/src/components/cloudConnectHandoff.ts +276 -0
- package/src/components/curl/ServiceCurlButton.tsx +19 -26
- package/src/components/diagnose/AgentCase.tsx +18 -5
- package/src/components/diagnose/AnalysisStory.test.tsx +308 -0
- package/src/components/diagnose/AnalysisStory.tsx +564 -0
- package/src/components/diagnose/DiagnoseContext.test.ts +10 -0
- package/src/components/diagnose/DiagnoseContext.tsx +26 -8
- package/src/components/diagnose/DiagnoseSurface.tsx +20 -14
- package/src/components/diagnose/InvestigationEvidencePane.story.test.tsx +771 -0
- package/src/components/diagnose/InvestigationEvidencePane.test.tsx +8 -33
- package/src/components/diagnose/InvestigationEvidencePane.tsx +809 -168
- package/src/components/diagnose/InvestigationResourceEvidence.test.tsx +30 -0
- package/src/components/diagnose/InvestigationResourceEvidence.tsx +20 -0
- package/src/components/diagnose/InvestigationView.tsx +493 -529
- package/src/components/diagnose/investigationCase.test.tsx +267 -129
- package/src/components/diagnose/investigationCase.ts +122 -77
- package/src/components/diagnose/investigationEvidence/adapters/changes.ts +60 -0
- package/src/components/diagnose/investigationEvidence/adapters/diagnose.test.ts +573 -0
- package/src/components/diagnose/investigationEvidence/adapters/diagnose.ts +935 -0
- package/src/components/diagnose/investigationEvidence/adapters/events.ts +96 -0
- package/src/components/diagnose/investigationEvidence/adapters/helm.test.ts +242 -0
- package/src/components/diagnose/investigationEvidence/adapters/helm.ts +208 -0
- package/src/components/diagnose/investigationEvidence/adapters/issues.ts +172 -0
- package/src/components/diagnose/investigationEvidence/adapters/logs.test.ts +283 -0
- package/src/components/diagnose/investigationEvidence/adapters/logs.ts +183 -0
- package/src/components/diagnose/investigationEvidence/adapters/permissions.test.ts +528 -0
- package/src/components/diagnose/investigationEvidence/adapters/permissions.ts +292 -0
- package/src/components/diagnose/investigationEvidence/adapters/prometheus.test.ts +1641 -0
- package/src/components/diagnose/investigationEvidence/adapters/prometheus.ts +769 -0
- package/src/components/diagnose/investigationEvidence/adapters/resource.test.ts +81 -0
- package/src/components/diagnose/investigationEvidence/adapters/resource.ts +285 -0
- package/src/components/diagnose/investigationEvidence/adapters/topology.ts +207 -0
- package/src/components/diagnose/investigationEvidence/builder.ts +427 -0
- package/src/components/diagnose/investigationEvidence/evidenceFixtures.ts +179 -0
- package/src/components/diagnose/investigationEvidence/identity.test.ts +632 -0
- package/src/components/diagnose/investigationEvidence/identity.ts +261 -0
- package/src/components/diagnose/investigationEvidence/index.ts +55 -0
- package/src/components/diagnose/investigationEvidence/observations.ts +978 -0
- package/src/components/diagnose/investigationEvidence/parse.ts +467 -0
- package/src/components/diagnose/investigationEvidence/projection.test.ts +2736 -0
- package/src/components/diagnose/investigationEvidence/types.ts +514 -0
- package/src/components/diagnose/investigationEvidencePresentation.test.ts +2 -0
- package/src/components/diagnose/investigationEvidencePresentation.ts +3 -0
- package/src/components/diagnose/investigationState.test.ts +316 -58
- package/src/components/diagnose/investigationState.ts +257 -44
- package/src/components/diagnose/investigationStory.test.ts +161 -0
- package/src/components/diagnose/investigationStory.ts +207 -0
- package/src/components/diagnose/parts.test.tsx +9 -6
- package/src/components/diagnose/parts.tsx +977 -193
- package/src/components/diagnose/storyParts.test.tsx +426 -0
- package/src/components/diagnose/target.test.ts +19 -0
- package/src/components/diagnose/target.ts +3 -2
- package/src/components/diagnose/toolCallLabel.test.ts +51 -0
- package/src/components/diagnose/toolCallLabel.ts +135 -0
- package/src/components/diagnose/useDisclosureReveal.ts +10 -11
- package/src/components/gitops/GitOpsView.tsx +7 -0
- package/src/components/gitops/RemoteDestinationCloudHint.tsx +26 -0
- package/src/components/helm/HelmCompareRoute.tsx +18 -4
- package/src/components/helm/HelmReleaseDrawer.tsx +11 -26
- package/src/components/helm/InstallWizard.tsx +8 -3
- package/src/components/home/MCPSetupDialog.tsx +15 -9
- package/src/components/portforward/PortForwardManager.tsx +6 -13
- package/src/components/resource/PrometheusCharts.overlays.test.ts +22 -0
- package/src/components/resource/PrometheusCharts.tsx +4 -3
- package/src/components/resource/PrometheusChartsGrid.render.test.tsx +99 -0
- package/src/components/resource/PrometheusChartsGrid.tsx +75 -35
- package/src/components/resource/RightsizingStrip.test.ts +38 -0
- package/src/components/resource/RightsizingStrip.tsx +15 -3
- package/src/components/resource/WorkloadMetricsHelpDialog.test.tsx +79 -0
- package/src/components/resource/WorkloadMetricsHelpDialog.tsx +145 -0
- package/src/components/resource/WorkloadMetricsSection.render.test.tsx +351 -0
- package/src/components/resource/WorkloadMetricsSection.tsx +480 -0
- package/src/components/resource/workloadMetricValues.test.ts +65 -0
- package/src/components/resource/workloadMetricValues.ts +30 -0
- package/src/components/resources/PodFilePreview.tsx +3 -3
- package/src/components/resources/ResourcesView.tsx +44 -6
- package/src/components/resources/renderers/NamespaceRenderer.tsx +7 -1
- package/src/components/resources/renderers/PodRenderer.tsx +4 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +15 -1
- package/src/components/resources/renderers/ServiceRenderer.tsx +2 -1
- package/src/components/resources/renderers/WorkloadRenderer.tsx +3 -0
- package/src/components/rightsizing/RightsizingScanView.tsx +21 -0
- package/src/components/settings/SettingsDialog.tsx +98 -16
- package/src/components/traffic/TrafficFlowList.tsx +229 -70
- package/src/components/traffic/TrafficGraph.tsx +6 -6
- package/src/components/traffic/TrafficView.tsx +10 -9
- package/src/components/traffic/trafficFilters.test.ts +28 -1
- package/src/components/traffic/trafficFilters.ts +31 -0
- package/src/components/ui/CommandPalette.tsx +10 -6
- package/src/components/ui/DiagnosticsOverlay.tsx +10 -6
- package/src/components/ui/Disclosure.tsx +47 -0
- package/src/components/ui/Markdown.tsx +23 -11
- package/src/components/ui/ShortcutHelpOverlay.tsx +3 -1
- package/src/components/ui/UpdateNotification.tsx +18 -2
- package/src/components/workload/WorkloadView.tsx +19 -8
- package/src/context/ConnectionContext.tsx +48 -3
- package/src/context/DiagnoseCustomization.tsx +1 -0
- package/src/index.css +36 -6
- package/src/utils/navigation.ts +1 -1
- package/src/utils/topology-namespace.test.ts +64 -0
- package/src/utils/topology-namespace.ts +18 -0
- package/src/components/diagnose/investigationEvidence.test.ts +0 -6617
- package/src/components/diagnose/investigationEvidence.ts +0 -5572
- package/src/components/gitops/GitOpsActions.tsx +0 -1
- package/src/components/gitops/GitOpsStatusBadge.tsx +0 -1
- package/src/components/gitops/ManagedResourcesList.tsx +0 -1
- package/src/components/gitops/SyncCountdown.tsx +0 -1
- package/src/components/gitops/index.ts +0 -4
- package/src/components/helm/ManifestDiffViewer.tsx +0 -69
- package/src/components/logs/JsonLogLine.tsx +0 -1
- package/src/components/logs/LogCore.tsx +0 -2
- package/src/components/logs/useLogBuffer.ts +0 -2
- package/src/components/logs/useLogSearch.ts +0 -1
- package/src/components/resource-drawer/ResourceDrawer.tsx +0 -216
- package/src/components/resources/drawer-components.tsx +0 -1
- package/src/components/resources/renderers/AlertRenderer.tsx +0 -1
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +0 -1
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +0 -1
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +0 -1
- package/src/components/resources/renderers/CertificateRenderer.tsx +0 -1
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +0 -1
- package/src/components/resources/renderers/ChallengeRenderer.tsx +0 -1
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +0 -1
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +0 -1
- package/src/components/resources/renderers/CronJobRenderer.tsx +0 -1
- package/src/components/resources/renderers/CronWorkflowRenderer.tsx +0 -1
- package/src/components/resources/renderers/EventRenderer.tsx +0 -1
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +0 -1
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/GatewayRenderer.tsx +0 -1
- package/src/components/resources/renderers/GenericRenderer.tsx +0 -1
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +0 -1
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +0 -1
- package/src/components/resources/renderers/IngressClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/IngressRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +0 -1
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +0 -1
- package/src/components/resources/renderers/JobRenderer.tsx +0 -1
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +0 -1
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +0 -1
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +0 -1
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +0 -1
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +0 -1
- package/src/components/resources/renderers/KustomizationRenderer.tsx +0 -1
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/LeaseRenderer.tsx +0 -1
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +0 -1
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +0 -1
- package/src/components/resources/renderers/OrderRenderer.tsx +0 -1
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +0 -1
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +0 -1
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +0 -1
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +0 -1
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +0 -1
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/SbomReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/SecretRenderer.tsx +0 -1
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +0 -1
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +0 -1
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/StorageClassRenderer.tsx +0 -1
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +0 -1
- package/src/components/resources/renderers/VPARenderer.tsx +0 -1
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +0 -1
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +0 -1
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +0 -1
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +0 -1
- package/src/components/resources/renderers/WorkflowRenderer.tsx +0 -1
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +0 -1
- package/src/components/resources/renderers/argo-cells.tsx +0 -1
- package/src/components/resources/renderers/certmanager-cells.tsx +0 -1
- package/src/components/resources/renderers/cnpg-cells.tsx +0 -1
- package/src/components/resources/renderers/eso-cells.tsx +0 -1
- package/src/components/resources/renderers/flux-cells.tsx +0 -1
- package/src/components/resources/renderers/index.ts +0 -94
- package/src/components/resources/renderers/istio-cells.tsx +0 -1
- package/src/components/resources/renderers/karpenter-cells.tsx +0 -1
- package/src/components/resources/renderers/keda-cells.tsx +0 -1
- package/src/components/resources/renderers/knative-cells.tsx +0 -1
- package/src/components/resources/renderers/kyverno-cells.tsx +0 -1
- package/src/components/resources/renderers/prometheus-cells.tsx +0 -1
- package/src/components/resources/renderers/traefik-cells.tsx +0 -1
- package/src/components/resources/renderers/trivy-cells.tsx +0 -1
- package/src/components/resources/renderers/trivy-shared.tsx +0 -1
- package/src/components/resources/renderers/velero-cells.tsx +0 -1
- package/src/components/resources/resource-utils-argo.ts +0 -2
- package/src/components/resources/resource-utils-certmanager.ts +0 -2
- package/src/components/resources/resource-utils-eso.ts +0 -2
- package/src/components/resources/resource-utils-flux.ts +0 -2
- package/src/components/resources/resource-utils-istio.ts +0 -2
- package/src/components/resources/resource-utils-karpenter.ts +0 -2
- package/src/components/resources/resource-utils-knative.ts +0 -2
- package/src/components/resources/resource-utils-kyverno.ts +0 -2
- package/src/components/resources/resource-utils-prometheus.ts +0 -2
- package/src/components/resources/resource-utils-traefik.ts +0 -1
- package/src/components/resources/resource-utils-trivy.ts +0 -2
- package/src/components/resources/resource-utils-velero.ts +0 -2
- package/src/components/shared/EditableYamlView.tsx +0 -24
- package/src/components/timeline/DiffViewer.tsx +0 -1
- package/src/components/traffic/index.ts +0 -3
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +0 -1
- package/src/components/ui/MetricsChart.tsx +0 -1
- package/src/components/ui/ResourceBar.tsx +0 -1
|
@@ -72,6 +72,9 @@ import { useRegisterShortcut } from '../../hooks/useKeyboardShortcuts'
|
|
|
72
72
|
import { CodeViewer } from '../ui/CodeViewer'
|
|
73
73
|
import { ArgoResourceDiffLoader } from './ArgoResourceDiffLoader'
|
|
74
74
|
import { RevisionMetaChip } from './RevisionMetaChip'
|
|
75
|
+
import { RemoteDestinationCloudHint } from './RemoteDestinationCloudHint'
|
|
76
|
+
|
|
77
|
+
const GITOPS_HEALTH_DOCS_URL = 'https://radarhq.io/docs/features/gitops#per-resource-health'
|
|
75
78
|
import type { GitOpsHistoryItem } from '@skyhook-io/k8s-ui'
|
|
76
79
|
|
|
77
80
|
const GITOPS_KINDS: APIResource[] = [
|
|
@@ -634,6 +637,9 @@ function GitOpsDetailView({ namespaces, onOpenResource, onOpenSettings }: GitOps
|
|
|
634
637
|
detail={detail}
|
|
635
638
|
insight={insightsQ.data ?? null}
|
|
636
639
|
insightLoading={insightsQ.isLoading}
|
|
640
|
+
healthDocsUrl={GITOPS_HEALTH_DOCS_URL}
|
|
641
|
+
remoteDestinationHint={<RemoteDestinationCloudHint />}
|
|
642
|
+
onOpenSettings={onOpenSettings}
|
|
637
643
|
renderRevisionMeta={
|
|
638
644
|
isArgoApp && insightsQ.data?.capabilities?.revisionMetadataAvailable
|
|
639
645
|
? (revision) => (
|
|
@@ -808,6 +814,7 @@ function GitOpsDetailView({ namespaces, onOpenResource, onOpenSettings }: GitOps
|
|
|
808
814
|
onQueryChange={setGraphSearch}
|
|
809
815
|
filters={graphFilters}
|
|
810
816
|
showToolbar={false}
|
|
817
|
+
issues={insightsQ.data?.issues}
|
|
811
818
|
/>
|
|
812
819
|
</div>
|
|
813
820
|
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useNavCustomization } from '../../context/NavCustomization'
|
|
2
|
+
import { useCapabilities } from '../../api/client'
|
|
3
|
+
import { openCloudFunnel } from '../CloudFunnelButton'
|
|
4
|
+
|
|
5
|
+
// The one sentence a standalone Radar adds after "this application deploys
|
|
6
|
+
// to another cluster": the thing the user just ran into is what Radar Cloud
|
|
7
|
+
// does. "Radar Cloud" opens the same dialog as the top-right button, so
|
|
8
|
+
// there is one pitch and one connect flow. That dialog connects THIS
|
|
9
|
+
// cluster; the destination is a second connect from Cloud, which is why the
|
|
10
|
+
// copy says "both clusters" and promises the view in Cloud, not here. Rendered only when that button
|
|
11
|
+
// is: the server decides whether there is anything to pitch (cloudConnect
|
|
12
|
+
// capability), and embedded hosts (Radar Cloud itself) mount neither.
|
|
13
|
+
export function RemoteDestinationCloudHint() {
|
|
14
|
+
const { embedded } = useNavCustomization()
|
|
15
|
+
const capabilities = useCapabilities()
|
|
16
|
+
if (embedded || !capabilities.data?.cloudConnect) return null
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
Connect both clusters to{' '}
|
|
20
|
+
<button type="button" onClick={openCloudFunnel} className="underline decoration-theme-border underline-offset-2 hover:text-theme-text-primary">
|
|
21
|
+
Radar Cloud
|
|
22
|
+
</button>{' '}
|
|
23
|
+
to see this app's resources together.
|
|
24
|
+
</>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -32,6 +32,8 @@ import { getHelmStatusColor, getKindBadgeColor, SEVERITY_BADGE } from '../../uti
|
|
|
32
32
|
import { formatDate } from './helm-utils'
|
|
33
33
|
import { RoleGatedPanel } from './RoleGatedPanel'
|
|
34
34
|
import { Tooltip } from '../ui/Tooltip'
|
|
35
|
+
import { TRANSITION_MENU, overlayExitMs, overlayTransitionStyle } from '../../utils/animation'
|
|
36
|
+
import { useAnimatedUnmount } from '../../hooks/useAnimatedUnmount'
|
|
35
37
|
|
|
36
38
|
type DiffTone = 'success' | 'warning' | 'error' | 'info' | 'neutral'
|
|
37
39
|
|
|
@@ -342,6 +344,9 @@ function RevisionSelect({
|
|
|
342
344
|
onChange: (revision: number) => void
|
|
343
345
|
}) {
|
|
344
346
|
const [open, setOpen] = useState(false)
|
|
347
|
+
// Presence outlives `open` by the menu exit so the list fades out in place;
|
|
348
|
+
// the outside-click / Escape listeners below still key off logical `open`.
|
|
349
|
+
const { shouldRender, isOpen } = useAnimatedUnmount(open, overlayExitMs('menu'))
|
|
345
350
|
const [activeIndex, setActiveIndex] = useState(0)
|
|
346
351
|
const rootRef = useRef<HTMLDivElement>(null)
|
|
347
352
|
const listboxId = useId()
|
|
@@ -371,12 +376,14 @@ function RevisionSelect({
|
|
|
371
376
|
if (open) setActiveIndex(selectedIndex)
|
|
372
377
|
}, [open, selectedIndex])
|
|
373
378
|
|
|
379
|
+
// The listbox mounts a commit after `open` (presence hook); without
|
|
380
|
+
// `shouldRender` here a reopen at an unchanged activeIndex never scrolls.
|
|
374
381
|
useEffect(() => {
|
|
375
|
-
if (!open) return
|
|
382
|
+
if (!open || !shouldRender) return
|
|
376
383
|
const revision = revisions[activeIndex]
|
|
377
384
|
if (!revision) return
|
|
378
385
|
document.getElementById(`${listboxId}-${revision.revision}`)?.scrollIntoView({ block: 'nearest' })
|
|
379
|
-
}, [activeIndex, listboxId, open, revisions])
|
|
386
|
+
}, [activeIndex, listboxId, open, shouldRender, revisions])
|
|
380
387
|
|
|
381
388
|
const selectRevision = (revision: number) => {
|
|
382
389
|
setOpen(false)
|
|
@@ -438,11 +445,18 @@ function RevisionSelect({
|
|
|
438
445
|
<span className="min-w-0 truncate">{selected ? formatRevisionOption(selected) : 'No revisions'}</span>
|
|
439
446
|
<ChevronDown className={clsx('h-3.5 w-3.5 shrink-0 text-theme-text-tertiary transition-transform', open && 'rotate-180')} />
|
|
440
447
|
</button>
|
|
441
|
-
{
|
|
448
|
+
{shouldRender && (
|
|
442
449
|
<div
|
|
443
450
|
id={listboxId}
|
|
444
451
|
role="listbox"
|
|
445
|
-
|
|
452
|
+
inert={!open || undefined}
|
|
453
|
+
className={clsx(
|
|
454
|
+
'absolute right-0 top-full z-50 mt-1 max-h-80 w-[min(36rem,calc(100vw-2rem))] origin-top-right overflow-y-auto rounded-lg border border-theme-border bg-theme-surface p-1 shadow-theme-lg',
|
|
455
|
+
TRANSITION_MENU,
|
|
456
|
+
isOpen ? 'opacity-100 translate-y-0 scale-100' : 'opacity-0 -translate-y-1 scale-[0.97]',
|
|
457
|
+
!open && 'pointer-events-none',
|
|
458
|
+
)}
|
|
459
|
+
style={overlayTransitionStyle(isOpen, 'menu')}
|
|
446
460
|
>
|
|
447
461
|
{revisions.map((revision, index) => {
|
|
448
462
|
const selectedRevision = revision.revision === value
|
|
@@ -4,7 +4,7 @@ import { FetchResult, useDockReservedHeight, compareVersions } from '@skyhook-io
|
|
|
4
4
|
import { startViewTransitionSafe } from '@skyhook-io/k8s-ui/utils/view-transition'
|
|
5
5
|
import { TRANSITION_DRAWER } from '../../utils/animation'
|
|
6
6
|
import { useRefreshAnimation } from '../../hooks/useRefreshAnimation'
|
|
7
|
-
import { X, Copy, Check, RefreshCw, Package, Code, History, Settings, Link2, Anchor, GitFork, BookOpen, ArrowUpCircle, Trash2, GitBranch, AlertTriangle, RotateCcw, Clock, GitCompare, ExternalLink,
|
|
7
|
+
import { X, Copy, Check, RefreshCw, Package, Code, History, Settings, Link2, Anchor, GitFork, BookOpen, ArrowUpCircle, Trash2, GitBranch, AlertTriangle, RotateCcw, Clock, GitCompare, ExternalLink, SlidersHorizontal, Eye, Loader2 } from 'lucide-react'
|
|
8
8
|
import yaml from 'yaml'
|
|
9
9
|
import { useNavigate } from 'react-router-dom'
|
|
10
10
|
import { clsx } from 'clsx'
|
|
@@ -25,6 +25,8 @@ import { ManifestViewer } from './ManifestViewer'
|
|
|
25
25
|
import { ValuesViewer } from './ValuesViewer'
|
|
26
26
|
import { OwnedResources } from './OwnedResources'
|
|
27
27
|
import { TrackChartSourceDialog } from './TrackChartSourceDialog'
|
|
28
|
+
import { Collapse, CollapseChevron } from '@skyhook-io/k8s-ui/components/ui/Collapse'
|
|
29
|
+
import { Disclosure } from '../ui/Disclosure'
|
|
28
30
|
|
|
29
31
|
interface HelmReleaseDrawerProps {
|
|
30
32
|
release: SelectedHelmRelease
|
|
@@ -117,7 +119,6 @@ export function HelmReleaseDrawer({ release, onClose, onNavigateToResource, isOp
|
|
|
117
119
|
const [showTrackSource, setShowTrackSource] = useState(false)
|
|
118
120
|
const [selectedVersion, setSelectedVersion] = useState<string | null>(null)
|
|
119
121
|
const [adjustValues, setAdjustValues] = useState(false)
|
|
120
|
-
const [renderAdjustValues, setRenderAdjustValues] = useState(false)
|
|
121
122
|
const [editedUpgradeYaml, setEditedUpgradeYaml] = useState('')
|
|
122
123
|
const [upgradeYamlError, setUpgradeYamlError] = useState<string | null>(null)
|
|
123
124
|
const [upgradeValuesSeeded, setUpgradeValuesSeeded] = useState(false)
|
|
@@ -357,7 +358,6 @@ export function HelmReleaseDrawer({ release, onClose, onNavigateToResource, isOp
|
|
|
357
358
|
setUpgradeProgress([])
|
|
358
359
|
setSelectedVersion(null)
|
|
359
360
|
setAdjustValues(false)
|
|
360
|
-
setRenderAdjustValues(false)
|
|
361
361
|
setEditedUpgradeYaml('')
|
|
362
362
|
editedUpgradeYamlRef.current = ''
|
|
363
363
|
setUpgradeYamlError(null)
|
|
@@ -383,7 +383,6 @@ export function HelmReleaseDrawer({ release, onClose, onNavigateToResource, isOp
|
|
|
383
383
|
return
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
setRenderAdjustValues(true)
|
|
387
386
|
setEditedUpgradeYaml('')
|
|
388
387
|
editedUpgradeYamlRef.current = ''
|
|
389
388
|
setUpgradeYamlError(null)
|
|
@@ -911,24 +910,15 @@ export function HelmReleaseDrawer({ release, onClose, onNavigateToResource, isOp
|
|
|
911
910
|
aria-controls="helm-upgrade-values-panel"
|
|
912
911
|
className="flex items-center gap-1.5 text-sm font-medium text-theme-text-secondary hover:text-theme-text-primary disabled:opacity-50"
|
|
913
912
|
>
|
|
914
|
-
<
|
|
913
|
+
<CollapseChevron open={adjustValues} className="w-4 h-4" />
|
|
915
914
|
<SlidersHorizontal className="w-3.5 h-3.5" />
|
|
916
915
|
Adjust your values (optional)
|
|
917
916
|
</button>
|
|
918
917
|
</Tooltip>
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
if (event.target !== event.currentTarget) return
|
|
924
|
-
if (event.propertyName !== 'grid-template-rows') return
|
|
925
|
-
if (!adjustValues) {
|
|
926
|
-
setRenderAdjustValues(false)
|
|
927
|
-
}
|
|
928
|
-
}}
|
|
929
|
-
>
|
|
930
|
-
<div className="overflow-hidden">
|
|
931
|
-
{renderAdjustValues && (
|
|
918
|
+
{/* unmountOnExit: the editor (uncontrolled textarea + seeded values)
|
|
919
|
+
must reset between opens, and the values query only runs while
|
|
920
|
+
the panel is open. */}
|
|
921
|
+
<Collapse open={adjustValues} unmountOnExit id="helm-upgrade-values-panel">
|
|
932
922
|
<div className="mt-2">
|
|
933
923
|
<p className="mb-2 text-xs text-theme-text-tertiary">
|
|
934
924
|
These are your current settings — edit them to carry into {targetVersion}. What you see here is exactly what gets applied.
|
|
@@ -988,9 +978,7 @@ export function HelmReleaseDrawer({ release, onClose, onNavigateToResource, isOp
|
|
|
988
978
|
</Tooltip>
|
|
989
979
|
</div>
|
|
990
980
|
</div>
|
|
991
|
-
|
|
992
|
-
</div>
|
|
993
|
-
</div>
|
|
981
|
+
</Collapse>
|
|
994
982
|
</div>
|
|
995
983
|
)}
|
|
996
984
|
{upgradeProgress.length > 0 && <ProgressLog entries={upgradeProgress} />}
|
|
@@ -1138,14 +1126,11 @@ function HelmOperationBanner({
|
|
|
1138
1126
|
</div>
|
|
1139
1127
|
<p className="mt-1 text-sm text-theme-text-secondary">{operation.message}</p>
|
|
1140
1128
|
{hasRawMessage && (
|
|
1141
|
-
<
|
|
1142
|
-
<summary className="cursor-pointer select-none text-xs font-medium text-theme-text-tertiary hover:text-theme-text-secondary">
|
|
1143
|
-
Show raw Helm error
|
|
1144
|
-
</summary>
|
|
1129
|
+
<Disclosure className="mt-2" summaryClassName="select-none text-xs font-medium text-theme-text-tertiary hover:text-theme-text-secondary" summary="Show raw Helm error">
|
|
1145
1130
|
<pre className="mt-2 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-theme-base/60 p-2 font-mono text-[11px] leading-relaxed text-theme-text-tertiary">
|
|
1146
1131
|
{rawMessage}
|
|
1147
1132
|
</pre>
|
|
1148
|
-
</
|
|
1133
|
+
</Disclosure>
|
|
1149
1134
|
)}
|
|
1150
1135
|
{operation.failureDescription && !hasRawMessage && (
|
|
1151
1136
|
<Tooltip content={operation.failureDescription} wrapperClassName="mt-1 flex">
|
|
@@ -9,6 +9,7 @@ import { useChartDetail, useNamespaces, useArtifactHubChart, installChartWithPro
|
|
|
9
9
|
import { useCanHelmAct } from '../../api/client'
|
|
10
10
|
import type { ChartSource, ChartDetail, ArtifactHubChartDetail } from '../../types'
|
|
11
11
|
import { YamlEditor } from '../ui/YamlEditor'
|
|
12
|
+
import { Collapse, CollapseChevron, useDisclosure } from '@skyhook-io/k8s-ui/components/ui/Collapse'
|
|
12
13
|
import { Tooltip } from '../ui/Tooltip'
|
|
13
14
|
import { Markdown } from '../ui/Markdown'
|
|
14
15
|
import { SEVERITY_BADGE, SEVERITY_TEXT } from '../../utils/badge-colors'
|
|
@@ -656,6 +657,7 @@ interface ValuesStepProps {
|
|
|
656
657
|
|
|
657
658
|
function ValuesStep({ valuesYaml, setValuesYaml, yamlError, setYamlError, chartDetail, source }: ValuesStepProps) {
|
|
658
659
|
const [showEditor, setShowEditor] = useState(false)
|
|
660
|
+
const editorDisclosure = useDisclosure(showEditor)
|
|
659
661
|
|
|
660
662
|
const isLocal = source === 'local'
|
|
661
663
|
const localDetail = chartDetail as ChartDetail | undefined
|
|
@@ -700,11 +702,12 @@ function ValuesStep({ valuesYaml, setValuesYaml, yamlError, setYamlError, chartD
|
|
|
700
702
|
{/* Collapsible editor section */}
|
|
701
703
|
<div className="border border-theme-border rounded-lg overflow-hidden">
|
|
702
704
|
<button
|
|
705
|
+
{...editorDisclosure.buttonProps}
|
|
703
706
|
onClick={() => setShowEditor(!showEditor)}
|
|
704
707
|
className="w-full flex items-center justify-between px-4 py-3 bg-theme-elevated/50 hover:bg-theme-elevated transition-colors"
|
|
705
708
|
>
|
|
706
709
|
<div className="flex items-center gap-2">
|
|
707
|
-
<
|
|
710
|
+
<CollapseChevron open={showEditor} className="w-4 h-4" />
|
|
708
711
|
<span className="text-sm font-medium text-theme-text-primary">
|
|
709
712
|
{hasValues ? (showEditor ? 'Hide' : 'Show') : 'Add'} configuration values
|
|
710
713
|
</span>
|
|
@@ -715,7 +718,9 @@ function ValuesStep({ valuesYaml, setValuesYaml, yamlError, setYamlError, chartD
|
|
|
715
718
|
</div>
|
|
716
719
|
</button>
|
|
717
720
|
|
|
718
|
-
{
|
|
721
|
+
{/* unmountOnExit: the Monaco editor is heavy and its instance should
|
|
722
|
+
not outlive a closed section; values live in `valuesYaml` above. */}
|
|
723
|
+
<Collapse open={showEditor} unmountOnExit id={editorDisclosure.panelId}>
|
|
719
724
|
<div className="p-4 border-t border-theme-border">
|
|
720
725
|
{/* Action buttons */}
|
|
721
726
|
<div className="flex items-center gap-3 mb-4">
|
|
@@ -777,7 +782,7 @@ function ValuesStep({ valuesYaml, setValuesYaml, yamlError, setYamlError, chartD
|
|
|
777
782
|
</div>
|
|
778
783
|
)}
|
|
779
784
|
</div>
|
|
780
|
-
|
|
785
|
+
</Collapse>
|
|
781
786
|
</div>
|
|
782
787
|
</div>
|
|
783
788
|
)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useRef, useEffect, useState, useCallback } from 'react'
|
|
2
|
-
import { X, Copy, Check, Radio, Terminal, MessageSquare, Code2,
|
|
2
|
+
import { X, Copy, Check, Radio, Terminal, MessageSquare, Code2, Pin } from 'lucide-react'
|
|
3
3
|
import { apiUrl, getAuthHeaders, getCredentialsMode } from '../../api/config'
|
|
4
4
|
import { MCP_TOOL_CATALOG } from './mcpToolCatalog'
|
|
5
5
|
import { Tooltip } from '../ui/Tooltip'
|
|
6
|
+
import { Disclosure } from '../ui/Disclosure'
|
|
6
7
|
|
|
7
8
|
interface MCPSetupDialogProps {
|
|
8
9
|
open: boolean
|
|
@@ -285,17 +286,22 @@ export function MCPSetupDialog({ open, onClose, mcpUrl }: MCPSetupDialogProps) {
|
|
|
285
286
|
{ icon: Terminal, name: 'OpenAI Codex', path: '~/.codex/config.toml', config: codexConfig },
|
|
286
287
|
{ icon: Terminal, name: 'Gemini CLI', path: '~/.gemini/settings.json', config: geminiConfig },
|
|
287
288
|
].map((agent) => (
|
|
288
|
-
<
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
289
|
+
<Disclosure
|
|
290
|
+
key={agent.name}
|
|
291
|
+
className="rounded-md border border-theme-border/50 bg-theme-base/30"
|
|
292
|
+
summaryClassName="gap-2 px-3 py-2 select-none hover:bg-theme-hover/50 rounded-md transition-colors"
|
|
293
|
+
summary={
|
|
294
|
+
<>
|
|
295
|
+
<agent.icon className="w-4 h-4 text-theme-text-tertiary" />
|
|
296
|
+
<span className="text-sm font-medium text-theme-text-primary">{agent.name}</span>
|
|
297
|
+
{agent.path && <span className="text-[10px] text-theme-text-tertiary ml-auto">{agent.path}</span>}
|
|
298
|
+
</>
|
|
299
|
+
}
|
|
300
|
+
>
|
|
295
301
|
<div className="px-3 pb-3 pt-1">
|
|
296
302
|
<CodeBlock>{agent.config}</CodeBlock>
|
|
297
303
|
</div>
|
|
298
|
-
</
|
|
304
|
+
</Disclosure>
|
|
299
305
|
))}
|
|
300
306
|
</div>
|
|
301
307
|
|
|
@@ -33,6 +33,7 @@ import { openExternal } from '../../utils/navigation'
|
|
|
33
33
|
import { apiUrl } from '../../api/config'
|
|
34
34
|
import { apiFetch, useCapabilities } from '../../api/client'
|
|
35
35
|
import { pluralize } from '@skyhook-io/k8s-ui'
|
|
36
|
+
import { Collapse } from '@skyhook-io/k8s-ui/components/ui/Collapse'
|
|
36
37
|
|
|
37
38
|
// --- Types -------------------------------------------------------------------
|
|
38
39
|
|
|
@@ -661,17 +662,10 @@ export function PortForwardPanel() {
|
|
|
661
662
|
keeping border and rounded corners correct at every intermediate height. */}
|
|
662
663
|
<div className="overflow-hidden rounded-xl bg-theme-surface dark:bg-theme-elevated border-2 border-skyhook-500/35 dark:border-skyhook-400/40 shadow-2xl dark:shadow-[0_24px_60px_-12px_rgba(0,0,0,0.75),0_10px_24px_-6px_rgba(0,0,0,0.45)]">
|
|
663
664
|
|
|
664
|
-
{/*
|
|
665
|
-
|
|
666
|
-
natural top-to-bottom reveal (header appears first, sessions follow). */}
|
|
667
|
-
<
|
|
668
|
-
className={clsx(
|
|
669
|
-
'grid transition-[grid-template-rows] duration-300',
|
|
670
|
-
isPanelOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'
|
|
671
|
-
)}
|
|
672
|
-
style={{ transitionTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)' }}
|
|
673
|
-
>
|
|
674
|
-
<div className="overflow-hidden">
|
|
665
|
+
{/* Height engine — Collapse animates grid-template-rows 0fr→1fr so the
|
|
666
|
+
shell grows from 0 to auto. Content clips from the bottom up, creating
|
|
667
|
+
a natural top-to-bottom reveal (header appears first, sessions follow). */}
|
|
668
|
+
<Collapse open={isPanelOpen}>
|
|
675
669
|
|
|
676
670
|
{/* Header — tinted green when all sessions running, red when any have failed. */}
|
|
677
671
|
<div
|
|
@@ -927,8 +921,7 @@ export function PortForwardPanel() {
|
|
|
927
921
|
)}
|
|
928
922
|
</div>
|
|
929
923
|
|
|
930
|
-
|
|
931
|
-
</div>{/* /grid-sizer */}
|
|
924
|
+
</Collapse>
|
|
932
925
|
</div>{/* /panel-shell */}
|
|
933
926
|
|
|
934
927
|
{/* Caret — rendered after the shell so it paints on top (z-10). Opaque fill
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { computeRequestLimitLines } from './PrometheusCharts'
|
|
3
|
+
|
|
4
|
+
describe('Pod request and limit overlays', () => {
|
|
5
|
+
it('does not draw a Pod limit when one runtime container is unlimited', () => {
|
|
6
|
+
const resource = { spec: { containers: [
|
|
7
|
+
{ resources: { requests: { cpu: '100m' }, limits: { cpu: '1' } } },
|
|
8
|
+
{ resources: { requests: { cpu: '200m' } } },
|
|
9
|
+
] } }
|
|
10
|
+
const lines = computeRequestLimitLines(resource, 'Pod', 'cpu')!
|
|
11
|
+
expect(lines).toHaveLength(1)
|
|
12
|
+
expect(lines[0].kind).toBe('request')
|
|
13
|
+
expect(lines[0].value).toBeCloseTo(0.3)
|
|
14
|
+
})
|
|
15
|
+
it('sums fully limited runtime containers at per-Pod scale', () => {
|
|
16
|
+
const resource = { spec: { replicas: 5, template: { spec: {
|
|
17
|
+
containers: [{ resources: { limits: { cpu: '1' } } }],
|
|
18
|
+
initContainers: [{ restartPolicy: 'Always', resources: { limits: { cpu: '200m' } } }],
|
|
19
|
+
} } } }
|
|
20
|
+
expect(computeRequestLimitLines(resource, 'Deployment', 'cpu')![0].value).toBeCloseTo(1.2)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -121,6 +121,7 @@ export function PrometheusCharts({ kind, namespace, name, showEmptyState = false
|
|
|
121
121
|
statusError={status?.error}
|
|
122
122
|
onConnect={() => connectMutation.mutate()}
|
|
123
123
|
connecting={connectMutation.isPending}
|
|
124
|
+
discovering={status?.discovering === true}
|
|
124
125
|
category={activeCategory}
|
|
125
126
|
onCategoryChange={setActiveCategory}
|
|
126
127
|
range={timeRange}
|
|
@@ -161,12 +162,12 @@ export function computeRequestLimitLines(
|
|
|
161
162
|
if (runtimeContainers.length === 0) return undefined
|
|
162
163
|
|
|
163
164
|
let reqSum = 0, reqAny = false
|
|
164
|
-
let limSum = 0,
|
|
165
|
+
let limSum = 0, limAll = true
|
|
165
166
|
for (const c of runtimeContainers) {
|
|
166
167
|
const req = readQuantity(c.resources?.requests?.[category], category)
|
|
167
168
|
const lim = readQuantity(c.resources?.limits?.[category], category)
|
|
168
169
|
if (req != null) { reqSum += req; reqAny = true }
|
|
169
|
-
if (lim != null) { limSum += lim
|
|
170
|
+
if (lim != null && lim > 0) { limSum += lim } else { limAll = false }
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
const lines: ReferenceLine[] = []
|
|
@@ -177,7 +178,7 @@ export function computeRequestLimitLines(
|
|
|
177
178
|
kind: 'request',
|
|
178
179
|
})
|
|
179
180
|
}
|
|
180
|
-
if (
|
|
181
|
+
if (limAll) {
|
|
181
182
|
lines.push({
|
|
182
183
|
value: limSum,
|
|
183
184
|
label: `limit ${formatRequestLimitLabel(limSum, category)}`,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { renderToString } from 'react-dom/server'
|
|
3
|
+
import { MemoryRouter } from 'react-router-dom'
|
|
4
|
+
import { NavCustomizationProvider } from '../../context/NavCustomization'
|
|
5
|
+
import type { WorkloadMetrics } from '../../api/workloadMetrics'
|
|
6
|
+
|
|
7
|
+
let connected = false
|
|
8
|
+
const ranges: string[] = []
|
|
9
|
+
const charts: { seriesLabels?: string[]; referenceLines?: unknown[] }[] = []
|
|
10
|
+
let workload: WorkloadMetrics
|
|
11
|
+
|
|
12
|
+
vi.mock('../../api/client', async (importActual) => ({
|
|
13
|
+
...(await importActual<typeof import('../../api/client')>()),
|
|
14
|
+
useAutoPromConnect: () => undefined,
|
|
15
|
+
usePrometheusStatus: () => ({ data: { connected, error: 'Backend query failed' } }),
|
|
16
|
+
usePrometheusConnect: () => ({ mutate: vi.fn(), isPending: false }),
|
|
17
|
+
usePrometheusResourceMetrics: (_kind: string, _ns: string, _name: string, _category: string, range: string) => {
|
|
18
|
+
ranges.push(range)
|
|
19
|
+
return { data: { unit: 'cores', result: { series: [{ labels: { pod: 'web-0' }, dataPoints: [{ timestamp: 100, value: 1 }, { timestamp: 160, value: 2 }] }] } } }
|
|
20
|
+
},
|
|
21
|
+
}))
|
|
22
|
+
vi.mock('../../api/workloadMetrics', () => ({ useWorkloadMetrics: () => ({ data: workload }) }))
|
|
23
|
+
vi.mock('./RestartChart', () => ({ RestartEventLane: () => null }))
|
|
24
|
+
vi.mock('./RightsizingStrip', () => ({ RightsizingStrip: () => null }))
|
|
25
|
+
vi.mock('@skyhook-io/k8s-ui/components/charts', async (importActual) => ({
|
|
26
|
+
...(await importActual<typeof import('@skyhook-io/k8s-ui/components/charts')>()),
|
|
27
|
+
AreaChart: (props: typeof charts[number]) => { charts.push(props); return <span>Chart</span> },
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
const { PrometheusChartsGrid } = await import('./PrometheusChartsGrid')
|
|
31
|
+
const resource = { spec: { template: { spec: { containers: [{ resources: { requests: { cpu: '100m' }, limits: { cpu: '200m' } } }] } } } }
|
|
32
|
+
const render = (url = '/workloads/Deployment/demo/web?tab=metrics', embedded = false) => renderToString(
|
|
33
|
+
<MemoryRouter initialEntries={[url]}><NavCustomizationProvider value={embedded ? { embedded: true, rightExtras: null } : {}}>
|
|
34
|
+
<PrometheusChartsGrid kind="Deployment" namespace="demo" name="web" resource={resource} />
|
|
35
|
+
</NavCustomizationProvider></MemoryRouter>,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
connected = false
|
|
40
|
+
ranges.length = 0
|
|
41
|
+
charts.length = 0
|
|
42
|
+
workload = { state: 'available', sources: [], pods: 1, podsTotal: 1, start: 100, end: 160, stepSeconds: 60, rateWindowSeconds: 300, history: {}, comparison: {}, panels: {} }
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('Expanded metrics recovery and scope', () => {
|
|
46
|
+
it('offers standalone setup without losing the backend error or retry', () => {
|
|
47
|
+
const html = render()
|
|
48
|
+
expect(html).toContain('Backend query failed')
|
|
49
|
+
expect(html).toContain('Discover Prometheus')
|
|
50
|
+
expect(html).toContain('Configure metrics')
|
|
51
|
+
expect(html).toContain('workload-metrics.md#what-each-chart-needs')
|
|
52
|
+
})
|
|
53
|
+
it('uses operator guidance instead of standalone settings when embedded', () => {
|
|
54
|
+
const html = render(undefined, true)
|
|
55
|
+
expect(html).toContain('Ask your operator')
|
|
56
|
+
expect(html).not.toContain('Configure metrics')
|
|
57
|
+
expect(html).toContain('Discover Prometheus')
|
|
58
|
+
})
|
|
59
|
+
it.each([
|
|
60
|
+
['/workloads/Deployment/demo/web?tab=metrics&metricsRange=3h', '3h'],
|
|
61
|
+
['/applications?app=demo&workload=Deployment%2Fdemo%2Fweb&tab=metrics&metricsRange=6h', '6h'],
|
|
62
|
+
['/applications?tab=metrics&metricsRange=invalid', '1h'],
|
|
63
|
+
['/applications?tab=metrics', '1h'],
|
|
64
|
+
])('reads and validates the URL range: %s', (url, expected) => {
|
|
65
|
+
connected = true
|
|
66
|
+
const html = render(url)
|
|
67
|
+
expect(html).toContain(`value="${expected}" selected=""`)
|
|
68
|
+
expect(ranges.length).toBeGreaterThan(0)
|
|
69
|
+
expect(ranges.every((range) => range === expected)).toBe(true)
|
|
70
|
+
})
|
|
71
|
+
it('leaves unverified charts accessible without template overlays or saturation', () => {
|
|
72
|
+
connected = true
|
|
73
|
+
const html = render()
|
|
74
|
+
expect(html).toContain('<details aria-label="Name-matched resource metrics">')
|
|
75
|
+
expect(html).toContain('Network and storage')
|
|
76
|
+
expect(html).not.toContain('% of limit')
|
|
77
|
+
expect(charts.length).toBeGreaterThan(0)
|
|
78
|
+
expect(charts.every((chart) => !chart.referenceLines?.length)).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
it('labels unlabeled request totals without changing aggregate and quantile labels', () => {
|
|
81
|
+
connected = true
|
|
82
|
+
const series = [{ labels: {}, dataPoints: [{ timestamp: 160, value: 1 }] }]
|
|
83
|
+
workload.panels.requests = { state: 'available', unit: 'requests/s', series }
|
|
84
|
+
workload.panels.p50 = { state: 'available', unit: 'seconds', series }
|
|
85
|
+
workload.panels.p95 = { state: 'available', unit: 'seconds', series }
|
|
86
|
+
workload.panels.cpu = { state: 'available', unit: 'cores', series: [{ ...series[0], labels: { aggregation: 'Workload' } }, { ...series[0], labels: { aggregation: 'Maximum Pod' } }] }
|
|
87
|
+
render()
|
|
88
|
+
expect(charts.map((chart) => chart.seriesLabels)).toContainEqual(['Requests / sec'])
|
|
89
|
+
expect(charts.map((chart) => chart.seriesLabels)).toContainEqual(['p50', 'p95'])
|
|
90
|
+
expect(charts.map((chart) => chart.seriesLabels)).toContainEqual(['Workload', 'Maximum Pod'])
|
|
91
|
+
})
|
|
92
|
+
it('retains template references on verified current-Pod charts', () => {
|
|
93
|
+
connected = true
|
|
94
|
+
workload.history.cpu = { mode: 'current-pods' }
|
|
95
|
+
workload.panels.cpu = { state: 'available', unit: 'cores', series: [{ labels: { pod: 'web-0' }, dataPoints: [{ timestamp: 160, value: 0.1 }] }] }
|
|
96
|
+
render()
|
|
97
|
+
expect(charts.some((chart) => chart.referenceLines?.some((line) => (line as { label: string }).label === 'Template request 100m'))).toBe(true)
|
|
98
|
+
})
|
|
99
|
+
})
|