@skyhook-io/radar-app 1.16.2 → 1.17.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 +5 -5
- package/src/App.tsx +60 -12
- package/src/RadarApp.tsx +27 -17
- package/src/api/client.goneResource.test.ts +49 -0
- package/src/api/client.ts +168 -55
- package/src/api/cnpg.ts +95 -0
- package/src/api/diagnose.test.ts +40 -0
- package/src/api/diagnose.ts +12 -1
- package/src/api/radarFeatures.render.test.tsx +19 -1
- package/src/api/radarFeatures.ts +3 -0
- package/src/api/timelineSource.ts +1 -1
- package/src/api/traffic.sources.test.ts +55 -0
- package/src/api/traffic.ts +173 -14
- package/src/components/NoClusterAccessBanner.tsx +68 -0
- package/src/components/applications/ApplicationsView.tsx +3 -0
- package/src/components/capacity/CapacityPoolDetail.tsx +18 -7
- package/src/components/capacity/CapacityView.test.tsx +31 -1
- package/src/components/capacity/shared.tsx +12 -21
- package/src/components/cnpg/CNPGClusterActivity.tsx +49 -0
- package/src/components/cnpg/CNPGClusterLogs.tsx +62 -0
- package/src/components/cnpg/CNPGDeclarations.tsx +311 -0
- package/src/components/cnpg/CNPGDetailPage.tsx +228 -0
- package/src/components/cnpg/CNPGDrawerTrail.tsx +37 -0
- package/src/components/cnpg/CNPGOperator.tsx +234 -0
- package/src/components/cnpg/CNPGOverview.tsx +307 -0
- package/src/components/cnpg/CNPGPooling.tsx +116 -0
- package/src/components/cnpg/CNPGProtection.tsx +320 -0
- package/src/components/cnpg/CNPGSummaryHost.tsx +143 -0
- package/src/components/cnpg/CNPGView.tsx +156 -0
- package/src/components/cnpg/paths.ts +13 -0
- package/src/components/cnpg/routes.test.ts +60 -0
- package/src/components/cnpg/routes.ts +112 -0
- package/src/components/cnpg/shared.tsx +290 -0
- package/src/components/cnpg/useCNPGSidebarWorkspace.ts +88 -0
- package/src/components/diagnose/AgentSetupNotice.test.tsx +58 -0
- package/src/components/diagnose/AgentSetupNotice.tsx +142 -51
- package/src/components/diagnose/ApplyDialog.ack.test.tsx +47 -0
- package/src/components/diagnose/ApplyDialog.test.tsx +59 -6
- package/src/components/diagnose/ApplyDialog.tsx +26 -28
- package/src/components/diagnose/DiagnoseContext.test.ts +43 -0
- package/src/components/diagnose/DiagnoseContext.tsx +136 -54
- package/src/components/diagnose/DiagnoseSurface.tsx +16 -3
- package/src/components/diagnose/InvestigationView.tsx +39 -2
- package/src/components/diagnose/LocalDiagnoseAction.tsx +40 -20
- package/src/components/diagnose/agentCatalog.ts +2 -2
- package/src/components/diagnose/diagnoseEvidenceTypes.ts +2 -0
- package/src/components/dock/TrafficFlowListTab.tsx +27 -6
- package/src/components/execution/BatchExecutionView.tsx +11 -11
- package/src/components/helm/ChartBrowser.tsx +9 -10
- package/src/components/helm/HelmCompareRoute.tsx +108 -115
- package/src/components/helm/HelmReleaseDrawer.tsx +26 -44
- package/src/components/helm/InstallWizard.tsx +5 -2
- package/src/components/helm/RevisionHistory.tsx +3 -2
- package/src/components/helm/TrackChartSourceDialog.tsx +10 -2
- package/src/components/helm/ValuesViewer.tsx +1 -1
- package/src/components/home/ClusterHealthCard.tsx +7 -0
- package/src/components/home/mcpToolCatalog.ts +1 -1
- package/src/components/resources/ResourceDetailDrawer.tsx +6 -0
- package/src/components/resources/ResourcesView.tsx +6 -38
- package/src/components/resources/renderers/KyvernoPolicyQueued.tsx +2 -1
- package/src/components/resources/renderers/NodeRenderer.tsx +7 -1
- package/src/components/resources/renderers/ServiceRenderer.test.tsx +81 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +19 -14
- package/src/components/settings/SettingsDialog.tsx +122 -19
- package/src/components/shared/LargeClusterNamespacePicker.tsx +11 -5
- package/src/components/timeline/TimelineList.test.ts +47 -29
- package/src/components/timeline/TimelineList.tsx +60 -11
- package/src/components/timeline/TimelineRetentionNote.test.tsx +49 -0
- package/src/components/timeline/TimelineRetentionNote.tsx +20 -0
- package/src/components/timeline/TimelineView.tsx +16 -2
- package/src/components/timeline/TimelineView.urlparams.test.ts +6 -0
- package/src/components/traffic/TrafficConnectionsTable.tsx +144 -0
- package/src/components/traffic/TrafficFilterSidebar.tsx +22 -1
- package/src/components/traffic/TrafficFlowListContext.tsx +12 -4
- package/src/components/traffic/TrafficFocusSearch.tsx +172 -0
- package/src/components/traffic/TrafficGraph.tsx +109 -26
- package/src/components/traffic/TrafficGraphTooLarge.tsx +150 -0
- package/src/components/traffic/TrafficView.tsx +582 -149
- package/src/components/traffic/trafficFilters.test.ts +398 -1
- package/src/components/traffic/trafficFilters.ts +408 -0
- package/src/components/whats-new/WhatsNew.test.ts +107 -28
- package/src/components/whats-new/WhatsNew.tsx +146 -86
- package/src/components/whats-new/WhatsNewDialog.test.tsx +132 -10
- package/src/components/whats-new/releaseNotes.ts +155 -24
- package/src/components/workload/WorkloadView.test.ts +2 -1
- package/src/components/workload/WorkloadView.tsx +165 -294
- package/src/context/TimelineCustomization.tsx +15 -0
- package/src/contexts/CapabilitiesContext.tsx +9 -1
- package/src/hooks/useGitOpsWriteGuard.test.tsx +457 -0
- package/src/hooks/useGitOpsWriteGuard.ts +193 -0
- package/src/hooks/useResolvedGitOpsOwner.ts +256 -0
- package/src/hooks/useResourceCounts.ts +49 -0
- package/src/components/helm/RoleGatedPanel.tsx +0 -47
|
@@ -1,38 +1,94 @@
|
|
|
1
1
|
import type { LucideIcon } from 'lucide-react'
|
|
2
|
-
import { Bot, GitCompareArrows, Layers, Network, Scale } from 'lucide-react'
|
|
2
|
+
import { Bot, GitCompareArrows, Layers, Network, RefreshCcw, Scale, ScrollText, SlidersHorizontal } from 'lucide-react'
|
|
3
3
|
import { compareVersions } from '../../utils/version'
|
|
4
4
|
|
|
5
5
|
export interface ReleaseHighlight {
|
|
6
6
|
id: string
|
|
7
7
|
icon: LucideIcon
|
|
8
8
|
title: string
|
|
9
|
+
/**
|
|
10
|
+
* Shown on a grid card, so at most 150 characters. Read next to other
|
|
11
|
+
* releases' highlights, so it must stand on its own.
|
|
12
|
+
*/
|
|
9
13
|
description: string
|
|
14
|
+
/**
|
|
15
|
+
* Fuller copy for when this highlight leads the dialog. Only a release's
|
|
16
|
+
* top highlight can ever lead (every other one scores at or below it), so
|
|
17
|
+
* only that one may set it.
|
|
18
|
+
*/
|
|
19
|
+
leadDescription?: string
|
|
20
|
+
/**
|
|
21
|
+
* 1-10, comparable across releases: it decides which highlights a user who
|
|
22
|
+
* skipped releases sees, and whether the dialog opens by itself.
|
|
23
|
+
* 9-10 a new capability that defines the release
|
|
24
|
+
* 7-8 a solid feature a typical user would go try now
|
|
25
|
+
* 4-6 a useful improvement
|
|
26
|
+
* 1-3 polish
|
|
27
|
+
* Most highlights are not 9s. When every card is a 7, every release
|
|
28
|
+
* interrupts the user, which is what the score exists to prevent.
|
|
29
|
+
*/
|
|
30
|
+
importance: number
|
|
10
31
|
/** In-app route the highlight's call to action opens. */
|
|
11
32
|
path?: string
|
|
12
33
|
cta?: string
|
|
13
|
-
/** Icon tile color, so the cards read apart at a glance. The lead card always uses the accent. */
|
|
14
|
-
tone?: HighlightTone
|
|
15
34
|
}
|
|
16
35
|
|
|
17
|
-
export type HighlightTone = 'violet' | 'teal' | 'emerald' | 'indigo'
|
|
18
|
-
|
|
19
36
|
export interface ReleaseNotes {
|
|
20
37
|
version: string
|
|
21
|
-
/** The first highlight leads the dialog at full width. */
|
|
22
38
|
highlights: ReleaseHighlight[]
|
|
23
39
|
improvements: string[]
|
|
24
|
-
releaseUrl: string
|
|
25
40
|
}
|
|
26
41
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
42
|
+
export const CHANGELOG_URL = 'https://radarhq.io/changelog'
|
|
43
|
+
|
|
44
|
+
// One entry per release that has something to announce. A user sees the
|
|
45
|
+
// entries for the releases they haven't seen yet, composed into one dialog, so
|
|
46
|
+
// a patch upgrade, or one that skips releases, still gets them. Every minor and
|
|
47
|
+
// major release must have its own entry: scripts/check-whats-new.sh refuses to
|
|
31
48
|
// release one without it.
|
|
32
49
|
export const RELEASE_NOTES: ReleaseNotes[] = [
|
|
50
|
+
{
|
|
51
|
+
version: 'v1.16.0',
|
|
52
|
+
highlights: [
|
|
53
|
+
{
|
|
54
|
+
id: 'restart-loop-issues',
|
|
55
|
+
icon: RefreshCcw,
|
|
56
|
+
title: 'One steady issue per crash loop',
|
|
57
|
+
description: 'A crash-looping container stays one issue until it recovers, with probe failures and exits as evidence. No more repeat alerts.',
|
|
58
|
+
leadDescription: 'A restarting container is one issue until it recovers, instead of a new one on every check, so no more repeat alerts and investigations. Probe failures, the last exit and how many replicas are affected sit on it as evidence, and severity follows impact.',
|
|
59
|
+
importance: 6,
|
|
60
|
+
path: '/issues',
|
|
61
|
+
cta: 'Open Issues',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'log-viewer',
|
|
65
|
+
icon: ScrollText,
|
|
66
|
+
title: 'Log viewer enhancements',
|
|
67
|
+
description: 'Levels from the logger itself, stack traces kept with their errors, Hide matching, and a chip for lines without a level.',
|
|
68
|
+
importance: 5,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'per-cluster-settings',
|
|
72
|
+
icon: SlidersHorizontal,
|
|
73
|
+
title: 'Separate settings for each cluster',
|
|
74
|
+
description: 'Running Radar locally, each kubeconfig context keeps its own Metrics, Argo CD and Cost connections.',
|
|
75
|
+
importance: 4,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
improvements: [
|
|
79
|
+
// The grid fills row by row: pair lines of similar length.
|
|
80
|
+
"A workload's Timeline shows only its own history",
|
|
81
|
+
"Timeline's Problems filter no longer hides crash loops",
|
|
82
|
+
'Live Traffic figures corrected for Cilium and Istio',
|
|
83
|
+
'Saved namespace picks survive a failing namespace list',
|
|
84
|
+
'Terminal tabs show and keep the context they opened for',
|
|
85
|
+
'YAML review flags a resource that changed after review',
|
|
86
|
+
'Helm chart meets the Pod Security restricted profile',
|
|
87
|
+
'Helm chart extraArgs passes through any Radar flag',
|
|
88
|
+
],
|
|
89
|
+
},
|
|
33
90
|
{
|
|
34
91
|
version: 'v1.15.0',
|
|
35
|
-
releaseUrl: 'https://github.com/skyhook-io/radar/releases/tag/v1.15.0',
|
|
36
92
|
highlights: [
|
|
37
93
|
{
|
|
38
94
|
id: 'ai-investigations',
|
|
@@ -40,14 +96,16 @@ export const RELEASE_NOTES: ReleaseNotes[] = [
|
|
|
40
96
|
title: 'AI investigations that show their work',
|
|
41
97
|
// No link: the investigations page redirects home when this run
|
|
42
98
|
// mode can't host local agents.
|
|
43
|
-
description: 'A clear verdict and the story of what broke, with the
|
|
99
|
+
description: 'A clear verdict and the story of what broke, with the evidence placed where the agent cites it. Runs on OpenCode too.',
|
|
100
|
+
leadDescription: 'A clear verdict and the story of what broke, with the charts, logs and config behind it placed where the agent cites them. Runs on OpenCode too, including AWS Bedrock.',
|
|
101
|
+
importance: 9,
|
|
44
102
|
},
|
|
45
103
|
{
|
|
46
104
|
id: 'gitops-manifest-diff',
|
|
47
105
|
icon: GitCompareArrows,
|
|
48
106
|
title: 'Full manifest diffs for Argo CD resources',
|
|
49
107
|
description: 'Compare the Git-rendered and live manifest of a drifted Argo CD resource, side by side or unified.',
|
|
50
|
-
|
|
108
|
+
importance: 8,
|
|
51
109
|
path: '/gitops',
|
|
52
110
|
cta: 'Open GitOps',
|
|
53
111
|
},
|
|
@@ -56,7 +114,7 @@ export const RELEASE_NOTES: ReleaseNotes[] = [
|
|
|
56
114
|
icon: Scale,
|
|
57
115
|
title: 'Rightsizing and cost data in MCP',
|
|
58
116
|
description: 'Agents get new optimized tools. Risks (OOM, limit conflicts and throttling) lead before cost-saving, in the UI as well.',
|
|
59
|
-
|
|
117
|
+
importance: 7,
|
|
60
118
|
path: '/cost/rightsizing',
|
|
61
119
|
cta: 'Open Rightsizing',
|
|
62
120
|
},
|
|
@@ -65,14 +123,14 @@ export const RELEASE_NOTES: ReleaseNotes[] = [
|
|
|
65
123
|
icon: Layers,
|
|
66
124
|
title: 'Deeper batch + AI/ML workloads support',
|
|
67
125
|
description: 'Dedicated pages for Kueue, JobSet and Ray resources, and Job and JobSet details now show why a queued job has no Pods.',
|
|
68
|
-
|
|
126
|
+
importance: 7,
|
|
69
127
|
},
|
|
70
128
|
{
|
|
71
129
|
id: 'rollout-traffic-topology',
|
|
72
130
|
icon: Network,
|
|
73
131
|
title: 'Canary and blue-green in Topology',
|
|
74
132
|
description: 'See canary and stable Pods, which Services route to each side, and the canary traffic weight.',
|
|
75
|
-
|
|
133
|
+
importance: 7,
|
|
76
134
|
path: '/topology',
|
|
77
135
|
cta: 'Open Topology',
|
|
78
136
|
},
|
|
@@ -105,17 +163,90 @@ export function releaseLine(version: string): string {
|
|
|
105
163
|
return version.replace(/^(v\d+\.\d+)\.\d+$/, '$1')
|
|
106
164
|
}
|
|
107
165
|
|
|
108
|
-
/** The
|
|
166
|
+
/** The entries at or below `version`, newest first: what that version's user has access to. */
|
|
167
|
+
function releasesUpTo(version: string, catalog: ReleaseNotes[]): ReleaseNotes[] {
|
|
168
|
+
return catalog
|
|
169
|
+
.filter(notes => (compareVersions(notes.version, version) ?? 1) <= 0)
|
|
170
|
+
.sort((a, b) => compareVersions(b.version, a.version) ?? 0)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The newest notes for a release at or below `version`. */
|
|
109
174
|
export function latestReleaseNotesFor(
|
|
110
175
|
version: string | undefined,
|
|
111
176
|
catalog: ReleaseNotes[] = RELEASE_NOTES,
|
|
112
177
|
): ReleaseNotes | undefined {
|
|
113
178
|
if (!version) return undefined
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
179
|
+
return releasesUpTo(version, catalog)[0]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The entries a user hasn't seen, newest first: those after `lastSeen`, up to
|
|
184
|
+
* the running version. A fresh install has nothing to compare against and gets
|
|
185
|
+
* none; an install that predates the seen record, or whose record isn't a
|
|
186
|
+
* version, gets them all.
|
|
187
|
+
*/
|
|
188
|
+
export function unreadReleases(
|
|
189
|
+
currentVersion: string,
|
|
190
|
+
lastSeen: string | null,
|
|
191
|
+
priorInstall: boolean,
|
|
192
|
+
catalog: ReleaseNotes[] = RELEASE_NOTES,
|
|
193
|
+
): ReleaseNotes[] {
|
|
194
|
+
const available = releasesUpTo(currentVersion, catalog)
|
|
195
|
+
if (lastSeen === null) return priorInstall ? available : []
|
|
196
|
+
return available.filter(notes => (compareVersions(notes.version, lastSeen) ?? 1) > 0)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// How much a highlight counts by how far back its release is. A user who
|
|
200
|
+
// skipped releases sees the best of them, weighted toward the newest; older
|
|
201
|
+
// releases than this list covers are left to the changelog.
|
|
202
|
+
export const RELEASE_WEIGHTS = [1, 0.75, 0.5]
|
|
203
|
+
export const MAX_CARDS = 5
|
|
204
|
+
export const MAX_LINES = 8
|
|
205
|
+
// The dialog opens by itself only when the cards it shows add up to this.
|
|
206
|
+
// A release of mostly 4-6s stays behind the unread dot; one with a few solid
|
|
207
|
+
// features, or a skipped release with them, opens it.
|
|
208
|
+
export const AUTO_OPEN_SCORE = 25
|
|
209
|
+
|
|
210
|
+
export interface ComposedHighlight extends ReleaseHighlight {
|
|
211
|
+
version: string
|
|
212
|
+
score: number
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface ComposedLine {
|
|
216
|
+
version: string
|
|
217
|
+
text: string
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface ComposedNotes {
|
|
221
|
+
/** The releases shown, newest first. */
|
|
222
|
+
versions: string[]
|
|
223
|
+
/** The first one leads the dialog at full width. */
|
|
224
|
+
highlights: ComposedHighlight[]
|
|
225
|
+
lines: ComposedLine[]
|
|
226
|
+
/** Sum of the shown highlights' scores. */
|
|
227
|
+
score: number
|
|
228
|
+
/** False when older unseen releases were left out. */
|
|
229
|
+
complete: boolean
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** One dialog from releases ordered newest first. */
|
|
233
|
+
export function composeReleaseNotes(releases: ReleaseNotes[]): ComposedNotes {
|
|
234
|
+
const shown = releases.slice(0, RELEASE_WEIGHTS.length)
|
|
235
|
+
// Built newest release first, in authored order, so the stable sort breaks
|
|
236
|
+
// ties the same way.
|
|
237
|
+
const ranked = shown
|
|
238
|
+
.flatMap((notes, i) => notes.highlights.map(h => ({ ...h, version: notes.version, score: h.importance * RELEASE_WEIGHTS[i] })))
|
|
239
|
+
.sort((a, b) => b.score - a.score)
|
|
240
|
+
const highlights = ranked.slice(0, MAX_CARDS)
|
|
241
|
+
const lines: ComposedLine[] = [
|
|
242
|
+
...ranked.slice(MAX_CARDS).map(h => ({ version: h.version, text: h.title })),
|
|
243
|
+
...shown.flatMap(notes => notes.improvements.map(text => ({ version: notes.version, text }))),
|
|
244
|
+
].slice(0, MAX_LINES)
|
|
245
|
+
return {
|
|
246
|
+
versions: shown.map(notes => notes.version),
|
|
247
|
+
highlights,
|
|
248
|
+
lines,
|
|
249
|
+
score: highlights.reduce((sum, h) => sum + h.score, 0),
|
|
250
|
+
complete: shown.length === releases.length,
|
|
119
251
|
}
|
|
120
|
-
return latest
|
|
121
252
|
}
|
|
@@ -2,7 +2,8 @@ import { describe, expect, it } from 'vitest'
|
|
|
2
2
|
|
|
3
3
|
import { gitOpsOwnerFromRelationships } from '@skyhook-io/k8s-ui'
|
|
4
4
|
import type { Relationships } from '../../types'
|
|
5
|
-
import {
|
|
5
|
+
import { supportsBatchExecution } from './WorkloadView'
|
|
6
|
+
import { findInheritedGitOpsLookupRef } from '../../hooks/useResolvedGitOpsOwner'
|
|
6
7
|
|
|
7
8
|
describe('findInheritedGitOpsLookupRef', () => {
|
|
8
9
|
it('follows a referenced ReplicaSet to its parent workload for inherited ownership', () => {
|