@skyhook-io/radar-app 1.12.2 → 1.13.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 +6 -6
- package/src/App.tsx +35 -15
- package/src/api/client.images.test.ts +63 -0
- package/src/api/client.ts +246 -39
- package/src/api/client.yaml.test.ts +3 -3
- package/src/api/version-check.test.ts +78 -0
- package/src/components/CloudConnectFlow.tsx +46 -26
- package/src/components/CloudFunnelButton.tsx +166 -129
- package/src/components/ConnectionErrorView.test.tsx +21 -1
- package/src/components/ConnectionErrorView.tsx +7 -8
- package/src/components/applications/ApplicationsView.tsx +10 -9
- package/src/components/audit/AuditView.tsx +6 -3
- package/src/components/audit/UpgradeReadinessView.test.ts +26 -2
- package/src/components/audit/UpgradeReadinessView.tsx +19 -9
- package/src/components/cost/ApplicationCostTab.test.ts +45 -0
- package/src/components/cost/ApplicationCostTab.tsx +115 -64
- package/src/components/cost/CostTrendChart.test.ts +65 -0
- package/src/components/cost/CostTrendChart.tsx +107 -17
- package/src/components/cost/CostView.test.ts +36 -1
- package/src/components/cost/CostView.tsx +133 -51
- package/src/components/cost/CurrentAllocationUse.tsx +8 -4
- package/src/components/cost/WorkloadCostTab.test.ts +50 -0
- package/src/components/cost/WorkloadCostTab.tsx +109 -61
- package/src/components/cost/source.test.ts +33 -0
- package/src/components/cost/source.ts +100 -0
- package/src/components/diagnose/DiagnoseSurface.tsx +14 -10
- package/src/components/diagnose/parts.test.tsx +12 -4
- package/src/components/diagnose/parts.tsx +19 -15
- package/src/components/gitops/GitOpsView.tsx +8 -3
- package/src/components/helm/HelmReleaseDrawer.tsx +4 -3
- package/src/components/helm/OwnedResources.tsx +10 -2
- package/src/components/home/ClusterHealthCard.test.ts +31 -0
- package/src/components/home/ClusterHealthCard.tsx +60 -1
- package/src/components/home/CostCard.tsx +3 -2
- package/src/components/home/HomeView.tsx +36 -11
- package/src/components/home/MCPSetupDialog.tsx +5 -4
- package/src/components/home/RadarVersionLine.test.tsx +145 -0
- package/src/components/home/RadarVersionLine.tsx +137 -0
- package/src/components/resources/PodFilesystemModal.tsx +54 -2
- package/src/components/resources/ResourcesView.tsx +31 -8
- package/src/components/resources/renderers/WorkloadRenderer.tsx +13 -5
- package/src/components/rightsizing/RightsizingScanView.tsx +36 -12
- package/src/components/rightsizing/copy.test.ts +19 -0
- package/src/components/settings/SettingsDialog.tsx +687 -107
- package/src/components/settings/settings-state.test.ts +42 -0
- package/src/components/settings/settings-state.ts +39 -0
- package/src/components/ui/ErrorBoundary.test.tsx +55 -0
- package/src/components/ui/ErrorBoundary.tsx +17 -2
- package/src/components/ui/UpdateNotification.test.tsx +49 -0
- package/src/components/ui/UpdateNotification.tsx +6 -2
- package/src/components/workload/WorkloadView.test.ts +60 -0
- package/src/components/workload/WorkloadView.tsx +270 -29
- package/src/contexts/CapabilitiesContext.test.tsx +29 -0
- package/src/contexts/CapabilitiesContext.tsx +7 -3
- package/src/index.css +11 -1
- package/src/utils/navigation.test.ts +45 -0
- package/src/utils/navigation.ts +5 -5
- package/src/utils/topology-selection.ts +3 -2
- package/src/utils/version.test.ts +37 -0
- package/src/utils/version.ts +56 -0
package/src/utils/navigation.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { apiUrl, getAuthHeaders, getCredentialsMode } from '../api/config'
|
|
2
|
-
import { apiVersionToGroup,
|
|
2
|
+
import { apiVersionToGroup, kindToPluralWithGroup } from '@skyhook-io/k8s-ui/utils/navigation'
|
|
3
3
|
import type { SelectedResource, Topology } from '@skyhook-io/k8s-ui/types/core'
|
|
4
4
|
import type { SearchHit } from '../api/client'
|
|
5
5
|
|
|
@@ -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, pluralToKind, refToSelectedResource, apiVersionToGroup } from '@skyhook-io/k8s-ui/utils/navigation'
|
|
17
|
+
export { kindToPlural, kindToPluralWithGroup, pluralToKind, 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([
|
|
@@ -48,7 +48,7 @@ export function getNetworkPolicyResourceTarget(topology: Topology | null): { kin
|
|
|
48
48
|
|
|
49
49
|
const group = networkPolicyGroup(node)
|
|
50
50
|
const target = {
|
|
51
|
-
kind:
|
|
51
|
+
kind: kindToPluralWithGroup(node.kind, group ?? ''),
|
|
52
52
|
...(group ? { group } : {}),
|
|
53
53
|
}
|
|
54
54
|
targets.set(`${target.kind}\u0000${target.group ?? ''}`, target)
|
|
@@ -96,13 +96,13 @@ export function resourcePath(resource: SelectedResource): string {
|
|
|
96
96
|
}
|
|
97
97
|
if (resource.group) params.set('apiGroup', resource.group)
|
|
98
98
|
const query = params.toString()
|
|
99
|
-
return `/resources/${
|
|
99
|
+
return `/resources/${kindToPluralWithGroup(resource.kind, resource.group ?? '')}${query ? `?${query}` : ''}`
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
const FULLSCREEN_RESOURCE_KINDS = new Set(['pods', 'deployments', 'statefulsets', 'daemonsets', 'jobs', 'cronjobs', 'nodes'])
|
|
103
103
|
|
|
104
104
|
export function relatedResourcePath(resource: SelectedResource): string {
|
|
105
|
-
const apiKind =
|
|
105
|
+
const apiKind = kindToPluralWithGroup(resource.kind, resource.group ?? '').toLowerCase()
|
|
106
106
|
if (FULLSCREEN_RESOURCE_KINDS.has(apiKind)) {
|
|
107
107
|
return buildWorkloadPath({ ...resource, kind: apiKind })
|
|
108
108
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SelectedResource, Topology, TopologyNode } from '@skyhook-io/k8s-ui/types/core'
|
|
2
|
-
import { apiVersionToGroup,
|
|
2
|
+
import { apiVersionToGroup, kindToPluralWithGroup } from '@skyhook-io/k8s-ui/utils/navigation'
|
|
3
3
|
|
|
4
4
|
function topologyNodeGroup(node: TopologyNode): string | undefined {
|
|
5
5
|
const apiVersionGroup = apiVersionToGroup(node.data.apiVersion as string | undefined)
|
|
@@ -18,10 +18,11 @@ export function findSelectedTopologyNode(
|
|
|
18
18
|
selectedResource: SelectedResource,
|
|
19
19
|
): TopologyNode | undefined {
|
|
20
20
|
const namespace = selectedResource.namespace || ''
|
|
21
|
+
const selectedKind = kindToPluralWithGroup(selectedResource.kind, selectedResource.group ?? '')
|
|
21
22
|
const candidates = topology?.nodes.filter(node =>
|
|
22
23
|
((node.data.namespace as string) || '') === namespace &&
|
|
23
24
|
node.name === selectedResource.name &&
|
|
24
|
-
(
|
|
25
|
+
(kindToPluralWithGroup(node.kind, topologyNodeGroup(node) ?? '') === selectedKind || node.kind === selectedResource.kind),
|
|
25
26
|
) ?? []
|
|
26
27
|
|
|
27
28
|
if (candidates.length === 0) return undefined
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
getVersionUpdateStatus,
|
|
4
|
+
IN_CLUSTER_UPGRADE_URL,
|
|
5
|
+
parseMajorMinor,
|
|
6
|
+
versionUpdateURL,
|
|
7
|
+
} from './version'
|
|
8
|
+
|
|
9
|
+
describe('Radar version helpers', () => {
|
|
10
|
+
it('parses major and minor versions with an optional v prefix', () => {
|
|
11
|
+
expect(parseMajorMinor('v1.12.3')).toEqual({ major: 1, minor: 12 })
|
|
12
|
+
expect(parseMajorMinor('dev')).toBeNull()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('grades available updates by distance from the latest release', () => {
|
|
16
|
+
expect(getVersionUpdateStatus('1.12.0', '1.12.1')).toEqual({ tier: 'patch' })
|
|
17
|
+
expect(getVersionUpdateStatus('1.11.4', '1.12.1')).toEqual({ tier: 'minor', minorVersionsBehind: 1 })
|
|
18
|
+
expect(getVersionUpdateStatus('1.10.4', '1.12.1')).toEqual({ tier: 'minor', minorVersionsBehind: 2 })
|
|
19
|
+
expect(getVersionUpdateStatus('1.9.4', '1.12.1')).toEqual({ tier: 'stale', minorVersionsBehind: 3 })
|
|
20
|
+
expect(getVersionUpdateStatus('0.12.4', '1.12.1')).toEqual({ tier: 'stale', majorVersionBehind: true })
|
|
21
|
+
expect(getVersionUpdateStatus('1.12.1-rc.1', '1.12.1')).toEqual({ tier: 'patch' })
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('does not flag invalid, current, or newer local builds', () => {
|
|
25
|
+
expect(getVersionUpdateStatus('dev', '1.12.1')).toEqual({ tier: 'none' })
|
|
26
|
+
expect(getVersionUpdateStatus('1.12.1', '1.12.1')).toEqual({ tier: 'none' })
|
|
27
|
+
expect(getVersionUpdateStatus('1.13.0', '1.12.1')).toEqual({ tier: 'none' })
|
|
28
|
+
expect(getVersionUpdateStatus('2.0.0', '1.12.1')).toEqual({ tier: 'none' })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('routes in-cluster upgrades to instructions instead of release notes', () => {
|
|
32
|
+
const releaseURL = 'https://github.com/skyhook-io/radar/releases/tag/v1.2.4'
|
|
33
|
+
expect(versionUpdateURL('in-cluster', releaseURL)).toBe(IN_CLUSTER_UPGRADE_URL)
|
|
34
|
+
expect(versionUpdateURL('local', releaseURL)).toBe(releaseURL)
|
|
35
|
+
expect(versionUpdateURL('cloud', releaseURL)).toBeUndefined()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { DeploymentMode } from '../types'
|
|
2
|
+
|
|
3
|
+
export interface MajorMinorVersion {
|
|
4
|
+
major: number
|
|
5
|
+
minor: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type VersionUpdateTier = 'none' | 'patch' | 'minor' | 'stale'
|
|
9
|
+
|
|
10
|
+
export interface VersionUpdateStatus {
|
|
11
|
+
tier: VersionUpdateTier
|
|
12
|
+
minorVersionsBehind?: number
|
|
13
|
+
majorVersionBehind?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const IN_CLUSTER_UPGRADE_URL = 'https://radarhq.io/docs/configuration/in-cluster'
|
|
17
|
+
|
|
18
|
+
export function versionUpdateURL(deploymentMode: DeploymentMode, releaseURL?: string): string | undefined {
|
|
19
|
+
if (deploymentMode === 'cloud') return undefined
|
|
20
|
+
return deploymentMode === 'in-cluster' ? IN_CLUSTER_UPGRADE_URL : releaseURL
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parseMajorMinor(version: string): MajorMinorVersion | null {
|
|
24
|
+
const match = /^v?(\d+)\.(\d+)/.exec(version.trim())
|
|
25
|
+
if (!match) return null
|
|
26
|
+
return { major: Number(match[1]), minor: Number(match[2]) }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseVersion(version: string): [major: number, minor: number, patch: number, prerelease: boolean] | null {
|
|
30
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(version.trim())
|
|
31
|
+
if (!match) return null
|
|
32
|
+
return [Number(match[1]), Number(match[2]), Number(match[3]), !!match[4]]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getVersionUpdateStatus(current: string, latest?: string): VersionUpdateStatus {
|
|
36
|
+
if (!latest) return { tier: 'none' }
|
|
37
|
+
|
|
38
|
+
const currentVersion = parseVersion(current)
|
|
39
|
+
const latestVersion = parseVersion(latest)
|
|
40
|
+
if (!currentVersion || !latestVersion) return { tier: 'none' }
|
|
41
|
+
|
|
42
|
+
const [currentMajor, currentMinor, currentPatch, currentPrerelease] = currentVersion
|
|
43
|
+
const [latestMajor, latestMinor, latestPatch, latestPrerelease] = latestVersion
|
|
44
|
+
|
|
45
|
+
if (latestMajor < currentMajor) return { tier: 'none' }
|
|
46
|
+
if (latestMajor > currentMajor) return { tier: 'stale', majorVersionBehind: true }
|
|
47
|
+
|
|
48
|
+
const minorVersionsBehind = latestMinor - currentMinor
|
|
49
|
+
if (minorVersionsBehind >= 3) return { tier: 'stale', minorVersionsBehind }
|
|
50
|
+
if (minorVersionsBehind > 0) return { tier: 'minor', minorVersionsBehind }
|
|
51
|
+
if (minorVersionsBehind < 0 || latestPatch < currentPatch) return { tier: 'none' }
|
|
52
|
+
if (latestPatch === currentPatch) {
|
|
53
|
+
return currentPrerelease && !latestPrerelease ? { tier: 'patch' } : { tier: 'none' }
|
|
54
|
+
}
|
|
55
|
+
return { tier: 'patch' }
|
|
56
|
+
}
|