@skyhook-io/radar-app 1.13.4 → 1.13.5

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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/RadarApp.tsx +5 -0
  3. package/src/api/client.ts +4 -0
  4. package/src/api/diagnose.ts +61 -15
  5. package/src/components/ConnectionErrorView.test.tsx +30 -0
  6. package/src/components/ConnectionErrorView.tsx +31 -19
  7. package/src/components/diagnose/AgentCase.tsx +131 -0
  8. package/src/components/diagnose/DiagnoseSurface.test.tsx +0 -20
  9. package/src/components/diagnose/DiagnoseSurface.tsx +32 -195
  10. package/src/components/diagnose/InvestigationEvidencePane.test.tsx +783 -7
  11. package/src/components/diagnose/InvestigationEvidencePane.tsx +774 -133
  12. package/src/components/diagnose/InvestigationView.tsx +222 -5
  13. package/src/components/diagnose/diagnoseEvidenceTypes.ts +22 -1
  14. package/src/components/diagnose/investigationCase.test.tsx +1568 -0
  15. package/src/components/diagnose/investigationCase.ts +439 -0
  16. package/src/components/diagnose/investigationEvidence.test.ts +1566 -151
  17. package/src/components/diagnose/investigationEvidence.ts +831 -43
  18. package/src/components/diagnose/investigationEvidenceKinds.ts +218 -0
  19. package/src/components/diagnose/investigationEvidencePresentation.test.ts +31 -0
  20. package/src/components/diagnose/investigationEvidencePresentation.ts +1 -0
  21. package/src/components/diagnose/investigationMetrics.test.ts +712 -0
  22. package/src/components/diagnose/investigationMetrics.ts +393 -0
  23. package/src/components/diagnose/investigationSourceFocus.ts +2 -0
  24. package/src/components/diagnose/investigationState.test.ts +322 -0
  25. package/src/components/diagnose/investigationState.ts +147 -25
  26. package/src/components/diagnose/parts.test.tsx +482 -0
  27. package/src/components/diagnose/parts.tsx +418 -41
  28. package/src/components/resource/PrometheusChartsGrid.tsx +181 -79
  29. package/src/components/resources/PodFilePreview.test.tsx +131 -0
  30. package/src/components/resources/PodFilePreview.tsx +394 -0
  31. package/src/components/resources/PodFilesystemModal.tsx +157 -67
  32. package/src/context/DiagnoseCustomization.test.tsx +26 -0
  33. package/src/context/DiagnoseCustomization.tsx +14 -2
  34. package/src/index.ts +1 -0
  35. package/src/utils/shell-safe.test.ts +25 -1
  36. package/src/utils/shell-safe.ts +16 -0
@@ -0,0 +1,218 @@
1
+ import {
2
+ Activity,
3
+ BellRing,
4
+ Boxes,
5
+ Bug,
6
+ ChartLine,
7
+ CheckCircle2,
8
+ CircleAlert,
9
+ Clock3,
10
+ FileClock,
11
+ KeyRound,
12
+ ListTree,
13
+ Network,
14
+ Package,
15
+ ScrollText,
16
+ ShieldAlert,
17
+ type LucideIcon,
18
+ } from "lucide-react";
19
+
20
+ import type { InvestigationEvidenceKind } from "./investigationEvidence";
21
+
22
+ /**
23
+ * Everything the pane needs to know about a kind of evidence, in one place.
24
+ *
25
+ * Five decisions that are easy to answer separately and wrong to forget, the
26
+ * sharpest being whether a card may contradict a model's all-clear: miss that
27
+ * one for a kind and a firing alert naming the workload sits under a green
28
+ * "no problem found" banner. This is a `Record` over the kind union with no
29
+ * optional fields, so the compiler refuses a new kind until each is decided.
30
+ *
31
+ * Not everything per-kind lives here. Rendering the card body, whether it can
32
+ * expand, how a subject is read off it and what a source excerpt shows are
33
+ * still dispatched where they are used, because each needs the observation's
34
+ * data rather than a constant.
35
+ */
36
+ export interface InvestigationEvidenceKindTraits {
37
+ /**
38
+ * A card of this kind can contradict an all-clear. Radar captured a fact
39
+ * about the investigated resource that says something is wrong right now,
40
+ * so a model concluding otherwise has to be qualified rather than trusted.
41
+ * False for kinds that describe context, configuration or a shape of the
42
+ * cluster: those are worth reading and are not evidence of a live problem.
43
+ */
44
+ adverse: boolean;
45
+ /**
46
+ * One card of this kind is one unambiguous subject, so citing its source
47
+ * promotes exactly that fact. A broad read (issues, inventory, events)
48
+ * yields many rows from one source and a citation cannot pick one of them.
49
+ */
50
+ focused: boolean;
51
+ /** The card carries enough detail to want the full width of a grid row. */
52
+ fullRow: boolean;
53
+ icon: LucideIcon;
54
+ /**
55
+ * One sentence about what this kind of evidence can and cannot establish,
56
+ * shown under the card. `changes` has its own tooltip instead, because its
57
+ * wording depends on whether the age was collected.
58
+ */
59
+ caveat: string | undefined;
60
+ }
61
+
62
+ export const EVIDENCE_KIND_TRAITS: Readonly<
63
+ Record<InvestigationEvidenceKind, InvestigationEvidenceKindTraits>
64
+ > = {
65
+ // Radar classified a live problem on the resource.
66
+ issue: {
67
+ adverse: true,
68
+ focused: false,
69
+ fullRow: false,
70
+ icon: CircleAlert,
71
+ caveat: undefined,
72
+ },
73
+ // A pod of the workload cannot start.
74
+ startup: {
75
+ adverse: true,
76
+ focused: false,
77
+ fullRow: false,
78
+ icon: ShieldAlert,
79
+ caveat: undefined,
80
+ },
81
+ // A container exited or is restarting.
82
+ crash: {
83
+ adverse: true,
84
+ focused: true,
85
+ fullRow: false,
86
+ icon: Bug,
87
+ caveat: undefined,
88
+ },
89
+ // The object's own status: unready replicas, a failed condition.
90
+ resource: {
91
+ adverse: true,
92
+ focused: true,
93
+ fullRow: false,
94
+ icon: Boxes,
95
+ caveat: undefined,
96
+ },
97
+ logs: {
98
+ adverse: true,
99
+ focused: true,
100
+ fullRow: true,
101
+ icon: ScrollText,
102
+ caveat: undefined,
103
+ },
104
+ events: {
105
+ adverse: true,
106
+ focused: false,
107
+ fullRow: true,
108
+ icon: Clock3,
109
+ caveat:
110
+ "Events support the timeline; proximity alone does not establish cause.",
111
+ },
112
+ // A change is a thing that happened, not a thing that is wrong.
113
+ changes: {
114
+ adverse: false,
115
+ focused: false,
116
+ fullRow: false,
117
+ icon: FileClock,
118
+ caveat: undefined,
119
+ },
120
+ dns: {
121
+ adverse: true,
122
+ focused: false,
123
+ fullRow: false,
124
+ icon: Activity,
125
+ caveat: undefined,
126
+ },
127
+ network: {
128
+ adverse: true,
129
+ focused: false,
130
+ fullRow: false,
131
+ icon: Network,
132
+ caveat: undefined,
133
+ },
134
+ relationships: {
135
+ adverse: false,
136
+ focused: false,
137
+ fullRow: false,
138
+ icon: Network,
139
+ caveat:
140
+ "This shows direct relationships Radar found, not an inferred blast radius.",
141
+ },
142
+ topology: {
143
+ adverse: false,
144
+ focused: false,
145
+ fullRow: false,
146
+ icon: Network,
147
+ caveat:
148
+ "This shows direct relationships Radar found, not an inferred blast radius.",
149
+ },
150
+ inventory: {
151
+ adverse: false,
152
+ focused: false,
153
+ fullRow: false,
154
+ icon: ListTree,
155
+ caveat: undefined,
156
+ },
157
+ // A receipt records a check that found nothing, so it can never contradict.
158
+ receipt: {
159
+ adverse: false,
160
+ focused: false,
161
+ fullRow: false,
162
+ icon: CheckCircle2,
163
+ caveat: undefined,
164
+ },
165
+ // A rule firing about this workload is the most literal statement Radar
166
+ // holds that something is wrong with it.
167
+ alerts: {
168
+ adverse: true,
169
+ focused: true,
170
+ fullRow: true,
171
+ icon: BellRing,
172
+ caveat:
173
+ "Instances are matched to this investigation by their Prometheus labels; a firing rule alone does not establish the cause.",
174
+ },
175
+ // A failed or stuck release is a live problem with the thing that owns the
176
+ // workload, not background colour.
177
+ helm: {
178
+ adverse: true,
179
+ focused: true,
180
+ fullRow: false,
181
+ icon: Package,
182
+ caveat: undefined,
183
+ },
184
+ // A permission verdict describes a grant, not a runtime failure: a
185
+ // ServiceAccount that cannot read Secrets may be exactly as intended.
186
+ permissions: {
187
+ adverse: false,
188
+ focused: true,
189
+ fullRow: false,
190
+ icon: KeyRound,
191
+ caveat:
192
+ "This is what RBAC grants the subject, not what the workload has exercised.",
193
+ },
194
+ // A chart is a measurement; the reading, not the kind, is what alarms.
195
+ metrics: {
196
+ adverse: false,
197
+ focused: true,
198
+ fullRow: true,
199
+ icon: ChartLine,
200
+ caveat: undefined,
201
+ },
202
+ };
203
+
204
+ /** Kinds whose card can contradict a model's all-clear. */
205
+ export function evidenceKindIsAdverse(kind: string): boolean {
206
+ return (
207
+ Object.hasOwn(EVIDENCE_KIND_TRAITS, kind) &&
208
+ EVIDENCE_KIND_TRAITS[kind as InvestigationEvidenceKind].adverse
209
+ );
210
+ }
211
+
212
+ /** Kinds a citation of the source can promote as one fact. */
213
+ export function evidenceKindIsFocused(kind: string): boolean {
214
+ return (
215
+ Object.hasOwn(EVIDENCE_KIND_TRAITS, kind) &&
216
+ EVIDENCE_KIND_TRAITS[kind as InvestigationEvidenceKind].focused
217
+ );
218
+ }
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
+ import type { HPADiagnosisState } from "@skyhook-io/k8s-ui";
2
3
  import {
3
4
  evidenceDisplaySnapshot,
4
5
  groupEvidenceCoverage,
@@ -445,3 +446,33 @@ describe("coverage presentation", () => {
445
446
  expect(groupEvidenceCoverage([])).toEqual([]);
446
447
  });
447
448
  });
449
+
450
+ describe("evidenceDisplaySnapshot scaler diagnosis", () => {
451
+ const scaledBy = (state: HPADiagnosisState) => [
452
+ {
453
+ kind: "HorizontalPodAutoscaler",
454
+ namespace: "dev",
455
+ name: "api-hpa",
456
+ hpaSummary: {
457
+ state,
458
+ summary: "summary",
459
+ bounds: { min: 1, max: 5, current: 5, desired: 5 },
460
+ },
461
+ },
462
+ ];
463
+ const withScaler = (state: HPADiagnosisState): InvestigationEvidenceData => ({
464
+ type: "resource",
465
+ resource,
466
+ resourceContext: { tier: "basic", scaledBy: scaledBy(state) },
467
+ warnings: [],
468
+ });
469
+
470
+ it("treats a change in the HPA's state as a new observation", () => {
471
+ expect(
472
+ evidenceDisplaySnapshot(observation(withScaler("limited_max"))),
473
+ ).not.toBe(evidenceDisplaySnapshot(observation(withScaler("ok"))));
474
+ expect(
475
+ evidenceDisplaySnapshot(observation(withScaler("limited_max"))),
476
+ ).toBe(evidenceDisplaySnapshot(observation(withScaler("limited_max"))));
477
+ });
478
+ });
@@ -30,6 +30,7 @@ export function evidenceDisplaySnapshot(
30
30
  // These summaries can carry live state even when the raw object is cached.
31
31
  status: statusWithoutObservationTimes(context?.statusSummary),
32
32
  workload: context?.workloadSummary,
33
+ scalers: context?.scaledBy,
33
34
  issues: context?.issueSummary,
34
35
  gitOps: data.gitOpsDiagnosis,
35
36
  warnings: data.warnings.map(warningWithoutElapsedTime),