@skyhook-io/radar-app 1.13.5 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/src/App.tsx +60 -24
- package/src/api/client.authRedirect.test.ts +12 -1
- package/src/api/client.prometheusStatus.test.ts +24 -0
- package/src/api/client.ts +321 -16
- package/src/api/diagnose.ts +1 -0
- 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 +179 -42
- package/src/components/CloudFunnelButton.tsx +107 -36
- package/src/components/SyncProgressPanel.tsx +73 -0
- package/src/components/audit/AuditView.tsx +2 -0
- package/src/components/cloudConnectHandoff.test.ts +140 -0
- package/src/components/cloudConnectHandoff.ts +124 -0
- package/src/components/diagnose/DiagnoseContext.tsx +2 -1
- 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 +54 -0
- package/src/components/diagnose/investigationEvidence/adapters/resource.ts +257 -0
- package/src/components/diagnose/investigationEvidence/adapters/topology.ts +207 -0
- package/src/components/diagnose/investigationEvidence/builder.ts +418 -0
- package/src/components/diagnose/investigationEvidence/evidenceFixtures.ts +179 -0
- package/src/components/diagnose/investigationEvidence/identity.test.ts +614 -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 +954 -0
- package/src/components/diagnose/investigationEvidence/parse.ts +467 -0
- package/src/components/diagnose/investigationEvidence/projection.test.ts +2703 -0
- package/src/components/diagnose/investigationEvidence/types.ts +512 -0
- package/src/components/diagnose/target.test.ts +19 -0
- package/src/components/diagnose/target.ts +3 -2
- package/src/components/gitops/GitOpsView.tsx +7 -0
- package/src/components/gitops/RemoteDestinationCloudHint.tsx +26 -0
- 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 +486 -0
- package/src/components/resource/workloadMetricValues.test.ts +65 -0
- package/src/components/resource/workloadMetricValues.ts +30 -0
- 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/WorkloadRenderer.tsx +3 -0
- package/src/components/rightsizing/RightsizingScanView.tsx +21 -0
- package/src/components/settings/SettingsDialog.tsx +94 -14
- 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/workload/WorkloadView.tsx +19 -8
- package/src/context/ConnectionContext.tsx +48 -3
- package/src/context/DiagnoseCustomization.tsx +1 -0
- package/src/index.css +17 -0
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createContext, useContext, useState, useCallback, useEffect, useRef, ReactNode } from 'react'
|
|
2
2
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
3
3
|
import { getApiBase } from '../api/config'
|
|
4
|
-
import { apiFetch } from '../api/client'
|
|
4
|
+
import { apiFetch, type SyncStatusSnapshot } from '../api/client'
|
|
5
5
|
|
|
6
6
|
export type ConnectionStateType = 'connected' | 'disconnected' | 'connecting'
|
|
7
7
|
|
|
@@ -12,6 +12,10 @@ export interface ConnectionState {
|
|
|
12
12
|
error?: string
|
|
13
13
|
errorType?: string // config, auth, auth-rejected, auth-plugin-stuck, rbac, network, timeout, tls, unknown
|
|
14
14
|
progressMessage?: string
|
|
15
|
+
// Per-kind readiness while the initial informer sync runs ('connecting'
|
|
16
|
+
// with informers started). Drives the progressive app shell — absent once
|
|
17
|
+
// connected or before informers exist.
|
|
18
|
+
syncStatus?: SyncStatusSnapshot
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
interface ConnectionStatusResponse extends ConnectionState {
|
|
@@ -157,7 +161,15 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
157
161
|
if (!firstConnect || cacheWarmAtMountRef.current) {
|
|
158
162
|
queryClient.invalidateQueries()
|
|
159
163
|
} else {
|
|
160
|
-
|
|
164
|
+
// Detail responses served during the progressive shell are partial —
|
|
165
|
+
// the server skips relationship computation while the topology cache
|
|
166
|
+
// is unavailable — and they resolve as successes, so the error-only
|
|
167
|
+
// sweep would leave an open drawer without its Related Resources
|
|
168
|
+
// forever (focus refetch is globally off). Refetching the resource
|
|
169
|
+
// detail keys is at most the open drawer, so it stays cheap.
|
|
170
|
+
queryClient.invalidateQueries({
|
|
171
|
+
predicate: (q) => q.state.status === 'error' || q.queryKey[0] === 'resource',
|
|
172
|
+
})
|
|
161
173
|
}
|
|
162
174
|
}, [queryClient])
|
|
163
175
|
|
|
@@ -195,12 +207,35 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
195
207
|
// while the retry is still running; the retry's own result supersedes it.
|
|
196
208
|
if (manualRetryPendingRef.current || autoRetryInFlightRef.current) return
|
|
197
209
|
const current = connectionRef.current
|
|
210
|
+
// The progressive shell renders real resource lists while still
|
|
211
|
+
// 'connecting'. A query client shared across cluster mounts may hold
|
|
212
|
+
// another cluster's data under identical keys — drop it before the
|
|
213
|
+
// shell can show it (the 'connected' path repeats this for the
|
|
214
|
+
// full-refresh case). Deliberately ahead of the SSE-generation guard:
|
|
215
|
+
// live connection_state frames bump the generation and drop the poll,
|
|
216
|
+
// and the shell must not spend the whole sync on another cluster's rows.
|
|
217
|
+
if (current.state === 'connecting' && data.state === 'connecting' &&
|
|
218
|
+
data.syncStatus && cacheWarmAtMountRef.current) {
|
|
219
|
+
cacheWarmAtMountRef.current = false
|
|
220
|
+
// Drop only cluster-scoped data. App chrome (auth identity, kubeconfig
|
|
221
|
+
// contexts) is not another cluster's data, and purging it flips
|
|
222
|
+
// authMePending back on — which holds the whole shell on the splash
|
|
223
|
+
// until /auth/me refetches, delaying the first progressive paint.
|
|
224
|
+
const keep = new Set(['connection-status', 'auth-me', 'contexts'])
|
|
225
|
+
queryClient.removeQueries({ predicate: (q) => !keep.has(String(q.queryKey[0])) })
|
|
226
|
+
}
|
|
198
227
|
if (!shouldApplyPolledConnection(
|
|
199
228
|
current.state,
|
|
200
229
|
data.state,
|
|
201
230
|
data.sseGenerationAtStart,
|
|
202
231
|
sseGenerationRef.current,
|
|
203
232
|
)) {
|
|
233
|
+
// SSE owns the state fields, but connection_state frames don't carry
|
|
234
|
+
// per-kind sync progress — merge it from the poll so the progressive
|
|
235
|
+
// shell keeps ticking while still connecting.
|
|
236
|
+
if (data.state === 'connecting' && data.syncStatus) {
|
|
237
|
+
setConnection(prev => prev.state === 'connecting' ? { ...prev, syncStatus: data.syncStatus } : prev)
|
|
238
|
+
}
|
|
204
239
|
return
|
|
205
240
|
}
|
|
206
241
|
const becameConnected = current.state !== 'connected' && data.state === 'connected'
|
|
@@ -211,11 +246,12 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
211
246
|
error: data.error,
|
|
212
247
|
errorType: data.errorType,
|
|
213
248
|
progressMessage: data.progressMessage,
|
|
249
|
+
syncStatus: data.syncStatus,
|
|
214
250
|
})
|
|
215
251
|
if (becameConnected) {
|
|
216
252
|
refreshCachesOnConnect()
|
|
217
253
|
}
|
|
218
|
-
}, [data, dataUpdatedAt, refreshCachesOnConnect])
|
|
254
|
+
}, [data, dataUpdatedAt, refreshCachesOnConnect, queryClient])
|
|
219
255
|
|
|
220
256
|
// Retry mutation
|
|
221
257
|
const retryMutation = useMutation({
|
|
@@ -231,6 +267,9 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
231
267
|
state: 'connecting',
|
|
232
268
|
error: undefined,
|
|
233
269
|
progressMessage: 'Connecting to cluster...',
|
|
270
|
+
// A retry is a fresh attempt — per-kind progress from the previous
|
|
271
|
+
// attempt would mislabel readiness until the next poll.
|
|
272
|
+
syncStatus: undefined,
|
|
234
273
|
}))
|
|
235
274
|
},
|
|
236
275
|
onSuccess: (result) => {
|
|
@@ -371,6 +410,12 @@ export function ConnectionProvider({ children }: { children: ReactNode }) {
|
|
|
371
410
|
if (prev.state === 'connected' && status.state === 'connecting') {
|
|
372
411
|
return prev
|
|
373
412
|
}
|
|
413
|
+
// connection_state frames don't carry per-kind sync progress — keep the
|
|
414
|
+
// last polled snapshot while still connecting, or each SSE progress
|
|
415
|
+
// frame would flicker the progressive shell back to the splash.
|
|
416
|
+
if (status.state === 'connecting' && !status.syncStatus && prev.state === 'connecting') {
|
|
417
|
+
return { ...status, syncStatus: prev.syncStatus }
|
|
418
|
+
}
|
|
374
419
|
return status
|
|
375
420
|
})
|
|
376
421
|
|
|
@@ -23,6 +23,7 @@ export type RenderInvestigationRunActions = (props: {
|
|
|
23
23
|
|
|
24
24
|
/** Render prop for the resource-level Investigate action. */
|
|
25
25
|
export type RenderDiagnoseAction = (ctx: {
|
|
26
|
+
/** Kubernetes Kind when loaded or discovered; otherwise its API resource name. */
|
|
26
27
|
kind: string;
|
|
27
28
|
/** Kubernetes API group; empty means core. */
|
|
28
29
|
group?: string;
|
package/src/index.css
CHANGED
|
@@ -21,6 +21,23 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
@layer components {
|
|
24
|
+
.metrics-layout {
|
|
25
|
+
container-type: inline-size;
|
|
26
|
+
container-name: metrics;
|
|
27
|
+
}
|
|
28
|
+
.metrics-chart-grid {
|
|
29
|
+
display: grid;
|
|
30
|
+
grid-template-columns: minmax(0, 1fr);
|
|
31
|
+
gap: 0.75rem;
|
|
32
|
+
}
|
|
33
|
+
@container metrics (min-width: 760px) {
|
|
34
|
+
.metrics-chart-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
35
|
+
.metrics-chart-wide { grid-column: 1 / -1; }
|
|
36
|
+
}
|
|
37
|
+
@container metrics (min-width: 1200px) {
|
|
38
|
+
.metrics-chart-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
39
|
+
.metrics-chart-wide { grid-column: auto; }
|
|
40
|
+
}
|
|
24
41
|
.investigation-assessment {
|
|
25
42
|
background: var(--color-investigation-assessment);
|
|
26
43
|
border-color: var(--color-investigation-assessment-border);
|
package/src/utils/navigation.ts
CHANGED
|
@@ -14,7 +14,7 @@ export function searchHitToSelectedResource(hit: SearchHit): SelectedResource {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// Re-export shared navigation utilities from @skyhook-io/k8s-ui.
|
|
17
|
-
export { kindToPlural, kindToPluralWithGroup, pluralToKind, refToSelectedResource, apiVersionToGroup } from '@skyhook-io/k8s-ui/utils/navigation'
|
|
17
|
+
export { kindToPlural, kindToPluralWithGroup, pluralToKind, knownKindForPluralWithGroup, refToSelectedResource, apiVersionToGroup } from '@skyhook-io/k8s-ui/utils/navigation'
|
|
18
18
|
export type { NavigateToResource } from '@skyhook-io/k8s-ui/utils/navigation'
|
|
19
19
|
|
|
20
20
|
const NETWORK_POLICY_TOPOLOGY_KINDS = new Set([
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { TopologyNode } from '@skyhook-io/k8s-ui/types/core'
|
|
3
|
+
import { isInNamespaceScope, scopeNodesToNamespaces } from './topology-namespace'
|
|
4
|
+
|
|
5
|
+
function node(id: string, kind: string, namespace?: string): TopologyNode {
|
|
6
|
+
return {
|
|
7
|
+
id,
|
|
8
|
+
kind,
|
|
9
|
+
name: id,
|
|
10
|
+
status: 'healthy',
|
|
11
|
+
data: namespace === undefined ? {} : { namespace },
|
|
12
|
+
} as TopologyNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const nodes = [
|
|
16
|
+
node('web', 'Deployment', 'prod'),
|
|
17
|
+
node('ing', 'Ingress', 'staging'),
|
|
18
|
+
node('db', 'StatefulSet', 'prod'),
|
|
19
|
+
node('node-1', 'Node'), // cluster-scoped
|
|
20
|
+
node('pv-1', 'PersistentVolume'), // cluster-scoped
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
describe('scopeNodesToNamespaces', () => {
|
|
24
|
+
it('returns every node when no namespace is selected', () => {
|
|
25
|
+
expect(scopeNodesToNamespaces(nodes, [])).toHaveLength(5)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('keeps only the selected namespace, plus cluster-scoped nodes', () => {
|
|
29
|
+
const scoped = scopeNodesToNamespaces(nodes, ['prod'])
|
|
30
|
+
expect(scoped.map(n => n.id)).toEqual(['web', 'db', 'node-1', 'pv-1'])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('drops a kind that exists only outside the scope', () => {
|
|
34
|
+
const scoped = scopeNodesToNamespaces(nodes, ['prod'])
|
|
35
|
+
expect(scoped.some(n => n.kind === 'Ingress')).toBe(false)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('accepts several namespaces at once', () => {
|
|
39
|
+
const scoped = scopeNodesToNamespaces(nodes, ['prod', 'staging'])
|
|
40
|
+
expect(scoped).toHaveLength(5)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('returns the same array identity when the scope is empty', () => {
|
|
44
|
+
expect(scopeNodesToNamespaces(nodes, [])).toBe(nodes)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('isInNamespaceScope', () => {
|
|
49
|
+
it('passes everything for a null scope', () => {
|
|
50
|
+
expect(isInNamespaceScope(node('x', 'Pod', 'any'), null)).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('always passes a node with no namespace', () => {
|
|
54
|
+
expect(isInNamespaceScope(node('node-1', 'Node'), new Set(['prod']))).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('treats an empty-string namespace as cluster-scoped', () => {
|
|
58
|
+
expect(isInNamespaceScope(node('n', 'Node', ''), new Set(['prod']))).toBe(true)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('rejects a namespaced node outside the scope', () => {
|
|
62
|
+
expect(isInNamespaceScope(node('ing', 'Ingress', 'staging'), new Set(['prod']))).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TopologyNode } from '@skyhook-io/k8s-ui/types/core'
|
|
2
|
+
|
|
3
|
+
// A node with no namespace is cluster-scoped (Node, PersistentVolume,
|
|
4
|
+
// Namespace, ...) and stays visible in every scope.
|
|
5
|
+
export function isInNamespaceScope(node: TopologyNode, nsSet: Set<string> | null): boolean {
|
|
6
|
+
if (!nsSet) return true
|
|
7
|
+
const ns = node.data?.namespace as string | undefined
|
|
8
|
+
return !ns || nsSet.has(ns)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Shared by the topology graph and the filter sidebar so the two cannot
|
|
12
|
+
// disagree about which nodes are in scope. Namespace carve-out only — kind
|
|
13
|
+
// filtering stays on the graph.
|
|
14
|
+
export function scopeNodesToNamespaces(nodes: TopologyNode[], namespaces: string[]): TopologyNode[] {
|
|
15
|
+
if (namespaces.length === 0) return nodes
|
|
16
|
+
const nsSet = new Set(namespaces)
|
|
17
|
+
return nodes.filter(node => isInNamespaceScope(node, nsSet))
|
|
18
|
+
}
|