@skyhook-io/radar-app 1.14.5 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -9
- package/package.json +6 -6
- package/src/App.tsx +142 -334
- package/src/RadarApp.tsx +3 -8
- package/src/api/client.admission.test.ts +34 -0
- package/src/api/client.jobset.test.ts +18 -0
- package/src/api/client.resource-identity.test.ts +44 -0
- package/src/api/client.rightsizing.test.ts +7 -3
- package/src/api/client.ts +267 -78
- package/src/api/preferences.test.ts +54 -0
- package/src/api/preferences.ts +38 -0
- package/src/api/usage-data.fixtures.ts +33 -0
- package/src/api/usage-data.ts +165 -0
- package/src/components/CloudFunnelButton.test.tsx +155 -0
- package/src/components/CloudFunnelButton.tsx +113 -63
- package/src/components/ContextSwitcher.tsx +13 -132
- package/src/components/SelfManagedStart.tsx +96 -0
- package/src/components/applications/ApplicationsView.identity.test.ts +39 -0
- package/src/components/applications/ApplicationsView.tsx +5 -3
- package/src/components/audit/AuditSettingsDialog.tsx +15 -11
- package/src/components/cloudConnectHandoff.test.ts +1 -1
- package/src/components/cloudConnectHandoff.ts +1 -1
- package/src/components/diagnose/ActivityTurn.tsx +3 -2
- package/src/components/diagnose/AgentControls.tsx +26 -3
- package/src/components/diagnose/ApplyDialog.tsx +4 -2
- package/src/components/diagnose/DiagnoseContext.tsx +1 -0
- package/src/components/diagnose/agentCatalog.ts +6 -0
- package/src/components/diagnose/launch.test.ts +23 -0
- package/src/components/diagnose/launch.ts +4 -0
- package/src/components/diagnose/parts.test.tsx +18 -0
- package/src/components/dialog-names.test.ts +69 -0
- package/src/components/dock/NodeTerminalTab.test.tsx +34 -0
- package/src/components/dock/NodeTerminalTab.tsx +5 -3
- package/src/components/dock/WorkloadLogsTab.test.tsx +66 -0
- package/src/components/dock/WorkloadLogsTab.tsx +2 -1
- package/src/components/execution/BatchExecutionView.render.test.tsx +2 -1
- package/src/components/execution/BatchExecutionView.test.ts +52 -1
- package/src/components/execution/BatchExecutionView.tsx +324 -65
- package/src/components/execution/JobSetAdmission.test.tsx +165 -0
- package/src/components/execution/JobSetAdmission.tsx +26 -0
- package/src/components/execution/JobSetMemberComparison.test.tsx +30 -0
- package/src/components/execution/JobSetMemberComparison.tsx +88 -0
- package/src/components/execution/batch-run-actions.test.ts +1 -0
- package/src/components/execution/batch-timeline.test.ts +3 -0
- package/src/components/execution/execution-definition.test.ts +15 -1
- package/src/components/execution/execution-definition.ts +4 -0
- package/src/components/execution/member-collection.render.test.tsx +142 -0
- package/src/components/gitops/GitOpsView.tsx +33 -2
- package/src/components/gitops/destination-toast.test.ts +33 -0
- package/src/components/gitops/destination-toast.ts +46 -0
- package/src/components/gitops/useDestinationCluster.ts +30 -0
- package/src/components/helm/TrackChartSourceDialog.tsx +25 -14
- package/src/components/home/HomeView.tsx +6 -2
- package/src/components/home/MCPSetupDialog.tsx +10 -0
- package/src/components/home/RadarVersionLine.test.tsx +14 -0
- package/src/components/home/RadarVersionLine.tsx +28 -3
- package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +75 -22
- package/src/components/logs/WorkloadLogsViewer.tsx +7 -4
- package/src/components/nav/PrimaryNavRail.test.tsx +10 -1
- package/src/components/nav/PrimaryNavRail.tsx +27 -8
- package/src/components/resource/PVCUsageBar.render.test.tsx +168 -0
- package/src/components/resource/PVCUsageBar.tsx +62 -16
- package/src/components/resource/PrometheusChartsGrid.render.test.tsx +1 -1
- package/src/components/resources/ResourcesView.tsx +3 -3
- package/src/components/resources/renderers/CAPIClusterRenderer.tsx +8 -0
- package/src/components/resources/renderers/JobAdmissionRenderers.test.tsx +18 -0
- package/src/components/resources/renderers/JobAdmissionRenderers.tsx +18 -0
- package/src/components/resources/renderers/KueueWorkloadRenderer.test.tsx +39 -0
- package/src/components/resources/renderers/KueueWorkloadRenderer.tsx +37 -0
- package/src/components/resources/renderers/RayClusterRenderer.test.tsx +18 -0
- package/src/components/resources/renderers/RayClusterRenderer.tsx +17 -0
- package/src/components/resources/renderers/RayJobRenderer.test.tsx +40 -0
- package/src/components/resources/renderers/RayJobRenderer.tsx +45 -0
- package/src/components/resources/renderers/RayServiceRenderer.test.tsx +69 -0
- package/src/components/resources/renderers/RayServiceRenderer.tsx +38 -0
- package/src/components/rightsizing/RightsizingScanView.tsx +34 -22
- package/src/components/rightsizing/copy.test.ts +7 -0
- package/src/components/rightsizing/model.test.ts +2 -1
- package/src/components/rightsizing/notices.test.tsx +4 -3
- package/src/components/settings/OperatorManagedNotice.tsx +17 -0
- package/src/components/settings/PrivacySection.test.tsx +84 -0
- package/src/components/settings/PrivacySection.tsx +140 -0
- package/src/components/settings/SettingsDialog.tsx +168 -92
- package/src/components/settings/controls.tsx +55 -0
- package/src/components/settings/settings-state.test.ts +18 -0
- package/src/components/settings/settings-state.ts +19 -0
- package/src/components/timeline/TimelineView.tsx +9 -3
- package/src/components/timeline/TimelineView.urlparams.test.ts +18 -1
- package/src/components/ui/ErrorBoundary.tsx +5 -0
- package/src/components/ui/Omnibar.tsx +16 -0
- package/src/components/ui/UpdateNotification.tsx +13 -9
- package/src/components/ui/command-items.ts +14 -3
- package/src/components/usage-data/UsageDataAsk.test.tsx +41 -0
- package/src/components/usage-data/UsageDataAsk.tsx +106 -0
- package/src/components/usage-data/UsageDataPrompt.test.ts +53 -0
- package/src/components/usage-data/UsageDataPrompt.tsx +148 -0
- package/src/components/useContextSwitchFlow.tsx +147 -0
- package/src/components/whats-new/WhatsNew.test.ts +110 -0
- package/src/components/whats-new/WhatsNew.tsx +423 -0
- package/src/components/whats-new/WhatsNewDialog.test.tsx +229 -0
- package/src/components/whats-new/check-whats-new.test.ts +65 -0
- package/src/components/whats-new/releaseNotes.ts +121 -0
- package/src/components/workload/WorkloadView.test.ts +17 -1
- package/src/components/workload/WorkloadView.tsx +47 -19
- package/src/context/ContextSwitchContext.tsx +18 -2
- package/src/context/NavCustomization.tsx +4 -58
- package/src/context/ThemeContext.tsx +3 -11
- package/src/hooks/useClusterLoadState.ts +2 -2
- package/src/hooks/useFavorites.ts +3 -5
- package/src/k8s-ui-exports.test.ts +73 -0
- package/src/utils/auditBadges.ts +1 -1
- package/src/utils/navigation.test.ts +34 -1
- package/src/utils/navigation.ts +28 -5
- package/src/utils/topology-selection.test.ts +56 -0
- package/src/utils/topology-selection.ts +9 -9
- package/src/utils/version.ts +11 -0
- package/src/components/ui/CommandPalette.tsx +0 -261
package/src/RadarApp.tsx
CHANGED
|
@@ -84,12 +84,7 @@ export interface RadarAppProps {
|
|
|
84
84
|
* prefer to share its client rather than nest two providers.
|
|
85
85
|
*/
|
|
86
86
|
queryClient?: QueryClient;
|
|
87
|
-
/**
|
|
88
|
-
* Slot-based customization of Radar's top nav. Use to inject host-app
|
|
89
|
-
* brand, replace the kubeconfig context picker with a product-level
|
|
90
|
-
* cluster switcher, and append items to the right action bar.
|
|
91
|
-
* See ./context/NavCustomization for the slot shape.
|
|
92
|
-
*/
|
|
87
|
+
/** Embedded layout and host navigation hooks for Radar Hub. */
|
|
93
88
|
navSlots?: NavCustomization;
|
|
94
89
|
/**
|
|
95
90
|
* Whether Radar may set the browser tab title (`document.title`) per view.
|
|
@@ -128,14 +123,14 @@ export interface RadarAppProps {
|
|
|
128
123
|
/**
|
|
129
124
|
* Initial route for `router: 'memory'` (ignored for 'browser'). Lets a host
|
|
130
125
|
* deep-link a specific view (e.g. '/topology') without owning the URL bar —
|
|
131
|
-
* used with `navSlots.
|
|
126
|
+
* used with `navSlots.embedded: true` to render a single per-cluster view
|
|
132
127
|
* chromeless under the host's own chrome (Radar Hub's per-cluster destinations).
|
|
133
128
|
*/
|
|
134
129
|
initialPath?: string;
|
|
135
130
|
/**
|
|
136
131
|
* Reports cluster-data warmup after the main connection is usable. Embedders
|
|
137
132
|
* with their own chrome (Radar Hub) can render this in their topbar while
|
|
138
|
-
* Radar runs with `navSlots.
|
|
133
|
+
* Radar runs with `navSlots.embedded: true`.
|
|
139
134
|
*/
|
|
140
135
|
onClusterLoadStateChange?: (state: ClusterLoadState) => void;
|
|
141
136
|
/**
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { useQuery } from '@tanstack/react-query'
|
|
3
|
+
import { ApiError, useKueueAdmission as admissionQuery } from './client'
|
|
4
|
+
|
|
5
|
+
vi.mock('@tanstack/react-query', async (original) => ({ ...await original<typeof import('@tanstack/react-query')>(), useQuery: vi.fn(() => ({})) }))
|
|
6
|
+
|
|
7
|
+
describe('Kueue admission polling', () => {
|
|
8
|
+
function options(settings?: Parameters<typeof admissionQuery>[3]) {
|
|
9
|
+
admissionQuery('ml', 'training', 'current', settings)
|
|
10
|
+
return vi.mocked(useQuery).mock.calls.at(-1)![0]
|
|
11
|
+
}
|
|
12
|
+
function interval(settings: Parameters<typeof admissionQuery>[3], state: any = {}) {
|
|
13
|
+
const poll = options(settings).refetchInterval as (query: any) => number | false
|
|
14
|
+
return poll({ state })
|
|
15
|
+
}
|
|
16
|
+
it('separates Job and JobSet identities and recreated roots', () => {
|
|
17
|
+
expect(options({ isJob: true }).queryKey).toEqual(['kueue-admission', 'batch', 'jobs', 'ml', 'training', 'current'])
|
|
18
|
+
expect(options({ isRayJob: true }).queryKey).toEqual(['kueue-admission', 'ray.io', 'rayjobs', 'ml', 'training', 'current'])
|
|
19
|
+
expect(options().queryKey).toEqual(['kueue-admission', 'jobset.x-k8s.io', 'jobsets', 'ml', 'training', 'current'])
|
|
20
|
+
})
|
|
21
|
+
it('polls quiet and terminal Jobs slowly, active admission quickly', () => {
|
|
22
|
+
expect(interval({ isJob: true })).toBe(30000)
|
|
23
|
+
expect(interval({ isJob: true, hinted: true })).toBe(5000)
|
|
24
|
+
expect(interval({ isJob: true }, { data: { workloads: [{}] } })).toBe(5000)
|
|
25
|
+
expect(interval({ isJob: true, hinted: true, terminal: true })).toBe(30000)
|
|
26
|
+
expect(interval(undefined)).toBe(5000)
|
|
27
|
+
})
|
|
28
|
+
it('stops on confirmed absence or denied requests, not transient outages or old absence', () => {
|
|
29
|
+
expect(interval({ isJob: true }, { data: { uid: 'current', installed: false } })).toBe(false)
|
|
30
|
+
expect(interval({ isJob: true, hinted: true }, { data: { uid: 'old', installed: false } })).toBe(5000)
|
|
31
|
+
expect(interval({ isJob: true }, { error: new ApiError('denied', 403) })).toBe(false)
|
|
32
|
+
expect(interval({ isJob: true, hinted: true }, { error: new ApiError('warming', 503) })).toBe(5000)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { useQuery } from '@tanstack/react-query'
|
|
3
|
+
import { useWorkloadRuns, type WorkloadRunsResponse } from './client'
|
|
4
|
+
|
|
5
|
+
vi.mock('@tanstack/react-query', async (original) => ({ ...await original<typeof import('@tanstack/react-query')>(), useQuery: vi.fn(() => ({})) }))
|
|
6
|
+
|
|
7
|
+
describe('JobSet member polling', () => {
|
|
8
|
+
it('refreshes an active selected member even when the displayed window is idle', () => {
|
|
9
|
+
useWorkloadRuns('jobsets', 'training', 'distributed', true, { refetchActive: true, role: 'prepare', selected: 'jobs/training/worker' })
|
|
10
|
+
const options = vi.mocked(useQuery).mock.calls.at(-1)![0]
|
|
11
|
+
const interval = options.refetchInterval as (query: { state: { data: WorkloadRunsResponse } }) => number
|
|
12
|
+
const run = { group: 'batch', kind: 'jobs', namespace: 'training', name: 'prepare', phase: 'Succeeded', active: false }
|
|
13
|
+
const response: WorkloadRunsResponse = { collection: 'members', runs: [run], selected: { ...run, name: 'worker', phase: 'Running', active: true }, total: 2, filteredTotal: 1, truncated: false }
|
|
14
|
+
expect(interval({ state: { data: response } })).toBe(5000)
|
|
15
|
+
expect(interval({ state: { data: { ...response, selected: run } } })).toBe(30000)
|
|
16
|
+
expect(interval({ state: { data: { ...response, runs: [{ ...run, active: true }], selected: undefined } } })).toBe(5000)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { useQuery } from '@tanstack/react-query'
|
|
3
|
+
import { useResourceAudit, useResourceEvents } from './client'
|
|
4
|
+
|
|
5
|
+
vi.mock('@tanstack/react-query', async (original) => ({
|
|
6
|
+
...await original<typeof import('@tanstack/react-query')>(),
|
|
7
|
+
useQuery: vi.fn(() => ({})),
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
afterEach(() => { vi.clearAllMocks(); vi.unstubAllGlobals() })
|
|
11
|
+
|
|
12
|
+
describe('resource history identity', () => {
|
|
13
|
+
it('separates same-name core and Volcano history in requests and cache keys', async () => {
|
|
14
|
+
const urls: URL[] = []
|
|
15
|
+
vi.stubGlobal('fetch', vi.fn(async (input: string) => {
|
|
16
|
+
urls.push(new URL(input, 'http://localhost'))
|
|
17
|
+
return new Response('[]', {status: 200})
|
|
18
|
+
}))
|
|
19
|
+
useResourceEvents('jobs', 'ml', 'shared', 'batch')
|
|
20
|
+
useResourceEvents('jobs', 'ml', 'shared', 'batch.volcano.sh')
|
|
21
|
+
const queries = vi.mocked(useQuery).mock.calls.map(call => call[0])
|
|
22
|
+
expect(queries[0].queryKey).not.toEqual(queries[2].queryKey)
|
|
23
|
+
expect(queries[1].queryKey).not.toEqual(queries[3].queryKey)
|
|
24
|
+
for (const query of queries) await (query.queryFn as () => Promise<unknown>)()
|
|
25
|
+
expect(urls.map(url => url.searchParams.get('group'))).toEqual(['batch', 'batch', 'batch.volcano.sh', 'batch.volcano.sh'])
|
|
26
|
+
expect(urls.every(url => url.searchParams.get('kind') === 'Job')).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('does not query an unresolved custom identity', () => {
|
|
30
|
+
useResourceEvents('Widget', 'ml', 'shared')
|
|
31
|
+
expect(vi.mocked(useQuery).mock.calls.every(call => call[0].enabled === false)).toBe(true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('preserves a core group and an exact audit group', async () => {
|
|
35
|
+
const fetchMock = vi.fn<typeof fetch>(async () => new Response('[]', {status: 200}))
|
|
36
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
37
|
+
useResourceEvents('pods', 'ml', 'shared', '')
|
|
38
|
+
await (vi.mocked(useQuery).mock.calls[0][0].queryFn as () => Promise<unknown>)()
|
|
39
|
+
expect(new URL(String(fetchMock.mock.calls[0][0]), 'http://localhost').searchParams.get('group')).toBe('')
|
|
40
|
+
useResourceAudit('IngressRoute', 'ml', 'shared', 'traefik.io')
|
|
41
|
+
await (vi.mocked(useQuery).mock.calls.at(-1)![0].queryFn as () => Promise<unknown>)()
|
|
42
|
+
expect(fetchMock).toHaveBeenLastCalledWith('/api/audit/resource/IngressRoute/ml/shared?group=traefik.io', expect.anything())
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { skipToken } from '@tanstack/react-query'
|
|
3
2
|
import { getRightsizingScanCacheConfig } from './client'
|
|
4
3
|
|
|
5
4
|
describe('rightsizing scan cache config', () => {
|
|
6
|
-
it('
|
|
5
|
+
it('retains result lookups briefly', () => {
|
|
7
6
|
const config = getRightsizingScanCacheConfig(['staging', 'default'], 'cluster-a')
|
|
8
7
|
|
|
9
|
-
expect(config.queryFn).toBe(skipToken)
|
|
10
8
|
expect(config.gcTime).toBe(5 * 60 * 1000)
|
|
11
9
|
})
|
|
12
10
|
|
|
11
|
+
it('isolates identities', () => {
|
|
12
|
+
expect(getRightsizingScanCacheConfig([], 'a', 'alice').queryKey).not.toEqual(getRightsizingScanCacheConfig([], 'a', 'bob').queryKey)
|
|
13
|
+
})
|
|
14
|
+
|
|
13
15
|
it('normalizes namespace order and isolates cluster and namespace scopes', () => {
|
|
14
16
|
const config = getRightsizingScanCacheConfig(['staging', 'default'], 'cluster-a')
|
|
15
17
|
|
|
16
18
|
expect(config.namespaceKey).toBe('default,staging')
|
|
17
19
|
expect(config.queryKey).toEqual([
|
|
18
20
|
'prometheus-rightsizing-scan',
|
|
21
|
+
'/api',
|
|
22
|
+
'',
|
|
19
23
|
'cluster-a',
|
|
20
24
|
'default,staging',
|
|
21
25
|
])
|