@skyhook-io/radar-app 1.8.6 → 1.8.8

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 (109) hide show
  1. package/README.md +7 -1
  2. package/package.json +12 -10
  3. package/src/App.tsx +278 -230
  4. package/src/RadarApp.tsx +91 -20
  5. package/src/api/client.argoResourceSync.test.ts +69 -0
  6. package/src/api/client.delta.test.ts +89 -0
  7. package/src/api/client.deltaSync.test.ts +216 -0
  8. package/src/api/client.rightsizing.test.ts +32 -0
  9. package/src/api/client.ts +1462 -246
  10. package/src/api/diagnose.ts +288 -0
  11. package/src/api/timelineSource.test.ts +217 -0
  12. package/src/api/timelineSource.ts +582 -0
  13. package/src/components/ConnectionErrorView.tsx +174 -70
  14. package/src/components/ContextSwitcher.tsx +13 -5
  15. package/src/components/applications/ApplicationsView.tsx +775 -80
  16. package/src/components/audit/AuditSettingsDialog.tsx +2 -2
  17. package/src/components/cost/ApplicationCostTab.test.ts +204 -0
  18. package/src/components/cost/ApplicationCostTab.tsx +571 -0
  19. package/src/components/cost/CostTrendChart.tsx +103 -72
  20. package/src/components/cost/CostView.test.ts +12 -0
  21. package/src/components/cost/CostView.tsx +494 -229
  22. package/src/components/cost/CostViewTabs.test.tsx +21 -0
  23. package/src/components/cost/CostViewTabs.tsx +40 -0
  24. package/src/components/cost/CurrentAllocationUse.test.ts +21 -0
  25. package/src/components/cost/CurrentAllocationUse.tsx +126 -0
  26. package/src/components/cost/WorkloadCostTab.test.ts +153 -0
  27. package/src/components/cost/WorkloadCostTab.tsx +372 -0
  28. package/src/components/cost/cloud-console.test.ts +39 -0
  29. package/src/components/cost/cloud-console.ts +81 -0
  30. package/src/components/cost/errors.ts +8 -0
  31. package/src/components/cost/format.test.ts +27 -0
  32. package/src/components/cost/format.ts +46 -0
  33. package/src/components/cost/kinds.ts +5 -0
  34. package/src/components/curl/ServiceCurlButton.tsx +2 -2
  35. package/src/components/diagnose/AISettings.tsx +116 -0
  36. package/src/components/diagnose/DiagnoseContext.tsx +491 -0
  37. package/src/components/diagnose/DiagnoseSurface.tsx +385 -0
  38. package/src/components/diagnose/Home.tsx +163 -0
  39. package/src/components/diagnose/InvestigationView.tsx +604 -0
  40. package/src/components/diagnose/LocalDiagnoseAction.tsx +150 -0
  41. package/src/components/diagnose/launch.ts +65 -0
  42. package/src/components/diagnose/parts.tsx +1689 -0
  43. package/src/components/dock/BottomDock.tsx +2 -3
  44. package/src/components/dock/WorkloadLogsTab.tsx +21 -5
  45. package/src/components/execution/BatchExecutionView.test.ts +170 -0
  46. package/src/components/execution/BatchExecutionView.tsx +1329 -0
  47. package/src/components/execution/batch-run-actions.test.ts +48 -0
  48. package/src/components/execution/batch-run-actions.ts +24 -0
  49. package/src/components/execution/batch-timeline.test.ts +57 -0
  50. package/src/components/execution/batch-timeline.ts +46 -0
  51. package/src/components/execution/execution-definition.test.ts +208 -0
  52. package/src/components/execution/execution-definition.ts +245 -0
  53. package/src/components/gitops/ArgoResourceDiffLoader.tsx +23 -0
  54. package/src/components/gitops/GitOpsView.tsx +81 -14
  55. package/src/components/gitops/RevisionMetaChip.tsx +63 -0
  56. package/src/components/helm/ChartBrowser.tsx +2 -3
  57. package/src/components/helm/HelmCompareRoute.tsx +1 -2
  58. package/src/components/helm/HelmReleaseDrawer.test.ts +17 -0
  59. package/src/components/helm/HelmReleaseDrawer.tsx +376 -43
  60. package/src/components/helm/HelmView.tsx +2 -3
  61. package/src/components/helm/InstallWizard.tsx +3 -5
  62. package/src/components/helm/ManifestDiffViewer.tsx +1 -31
  63. package/src/components/helm/TrackChartSourceDialog.tsx +48 -4
  64. package/src/components/helm/ValuesDiffPreview.tsx +17 -7
  65. package/src/components/home/CostCard.tsx +21 -36
  66. package/src/components/home/HomeView.tsx +17 -15
  67. package/src/components/home/MCPSetupDialog.tsx +1 -1
  68. package/src/components/home/mcpToolCatalog.ts +2 -2
  69. package/src/components/issues/IssuesPane.tsx +9 -1
  70. package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +135 -0
  71. package/src/components/portforward/PortForwardButton.tsx +2 -2
  72. package/src/components/portforward/PortForwardManager.tsx +19 -14
  73. package/src/components/resource/PrometheusChartsGrid.tsx +6 -80
  74. package/src/components/resource/RightsizingStrip.test.ts +109 -0
  75. package/src/components/resource/RightsizingStrip.tsx +320 -127
  76. package/src/components/resources/ImageFilesystemModal.tsx +2 -2
  77. package/src/components/resources/PodFilesystemModal.tsx +2 -3
  78. package/src/components/resources/ResourceDetailDrawer.tsx +2 -0
  79. package/src/components/resources/ResourcesView.tsx +13 -4
  80. package/src/components/resources/renderers/CronWorkflowRenderer.tsx +1 -0
  81. package/src/components/resources/renderers/index.ts +1 -0
  82. package/src/components/rightsizing/RightsizingScanView.tsx +938 -0
  83. package/src/components/rightsizing/copy.test.ts +56 -0
  84. package/src/components/rightsizing/model.test.ts +227 -0
  85. package/src/components/rightsizing/model.ts +158 -0
  86. package/src/components/rightsizing/presentation.test.ts +104 -0
  87. package/src/components/rightsizing/presentation.ts +94 -0
  88. package/src/components/settings/MyPermissionsDialog.tsx +66 -116
  89. package/src/components/settings/SettingsDialog.tsx +1290 -283
  90. package/src/components/shared/LargeClusterNamespacePicker.tsx +2 -2
  91. package/src/components/timeline/LocalTimelineScrubber.tsx +212 -0
  92. package/src/components/timeline/RetainedTimelineScrubber.tsx +311 -0
  93. package/src/components/timeline/TimelineList.tsx +81 -13
  94. package/src/components/timeline/TimelineView.tsx +842 -26
  95. package/src/components/timeline/TimelineView.urlparams.test.ts +335 -0
  96. package/src/components/traffic/TrafficFilterSidebar.tsx +2 -2
  97. package/src/components/traffic/TrafficView.tsx +8 -4
  98. package/src/components/ui/CommandPalette.tsx +2 -2
  99. package/src/components/workload/WorkloadView.tsx +912 -245
  100. package/src/context/ConnectionContext.tsx +109 -11
  101. package/src/context/DiagnoseCustomization.tsx +42 -0
  102. package/src/context/TimelineSource.tsx +50 -0
  103. package/src/hooks/useClusterLoadState.ts +73 -0
  104. package/src/hooks/useEventSource.ts +6 -0
  105. package/src/index.css +162 -1
  106. package/src/index.ts +12 -0
  107. package/src/main.tsx +1 -1
  108. package/src/types/clusterLoadState.ts +33 -0
  109. package/src/utils/navigation.ts +10 -0
@@ -5,6 +5,7 @@ import { useAuditSettings, useUpdateAuditSettings, useAudit, useCloudRole } from
5
5
  import type { CheckMeta } from '@skyhook-io/k8s-ui'
6
6
  import { validateRFC1123Label, type ValidationResult } from '@skyhook-io/k8s-ui/utils/validators'
7
7
  import { Tooltip } from '../ui/Tooltip'
8
+ import { Input } from '@skyhook-io/k8s-ui'
8
9
 
9
10
  interface AuditSettingsDialogProps {
10
11
  namespaces: string[]
@@ -118,8 +119,7 @@ export function AuditSettingsDialog({ namespaces, onClose }: AuditSettingsDialog
118
119
  </div>
119
120
 
120
121
  <div className="flex gap-2">
121
- <input
122
- type="text"
122
+ <Input
123
123
  value={newNs}
124
124
  onChange={e => setNewNs(e.target.value)}
125
125
  onKeyDown={e => { if (e.key === 'Enter') addNamespace() }}
@@ -0,0 +1,204 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { AppWorkload } from '@skyhook-io/k8s-ui'
3
+ import { applicationCostWorkloads, getApplicationCostState } from './ApplicationCostTab'
4
+ import {
5
+ ApiError,
6
+ type OpenCostApplicationCostResponse,
7
+ type OpenCostApplicationCostTrendResponse,
8
+ } from '../../api/client'
9
+
10
+ describe('getApplicationCostState', () => {
11
+ it('keeps current app cost visible when historical owner metrics are missing', () => {
12
+ const current: OpenCostApplicationCostResponse = {
13
+ available: true,
14
+ partial: true,
15
+ totals: {
16
+ hourlyCost: 0.4,
17
+ cpuCost: 0.25,
18
+ memoryCost: 0.15,
19
+ replicas: 3,
20
+ cpuUsageAvailable: true,
21
+ memoryUsageAvailable: true,
22
+ cpuAllocationUse: 40,
23
+ memoryAllocationUse: 60,
24
+ },
25
+ coverage: {
26
+ total: 3,
27
+ included: 2,
28
+ unavailable: [
29
+ {
30
+ kind: 'Deployment',
31
+ namespace: 'prod',
32
+ name: 'worker',
33
+ reason: 'no_metrics',
34
+ },
35
+ ],
36
+ },
37
+ workloads: [],
38
+ }
39
+ const trend: OpenCostApplicationCostTrendResponse = {
40
+ available: false,
41
+ reason: 'no_metrics',
42
+ range: '24h',
43
+ coverage: { total: 3, included: 0 },
44
+ }
45
+
46
+ expect(
47
+ getApplicationCostState(current, trend, {
48
+ currentLoading: false,
49
+ trendLoading: false,
50
+ }),
51
+ ).toBe('partial_missing_history')
52
+ })
53
+
54
+ it('uses historical data when current app metrics are absent but history exists', () => {
55
+ const current: OpenCostApplicationCostResponse = {
56
+ available: false,
57
+ reason: 'no_metrics',
58
+ totals: {
59
+ hourlyCost: 0,
60
+ cpuCost: 0,
61
+ memoryCost: 0,
62
+ replicas: 0,
63
+ cpuUsageAvailable: false,
64
+ memoryUsageAvailable: false,
65
+ cpuAllocationUse: 0,
66
+ memoryAllocationUse: 0,
67
+ },
68
+ coverage: { total: 2, included: 0 },
69
+ }
70
+ const trend: OpenCostApplicationCostTrendResponse = {
71
+ available: true,
72
+ range: '7d',
73
+ windowTotalCost: 2,
74
+ dataPoints: [
75
+ { timestamp: 1700000000, value: 0.2 },
76
+ { timestamp: 1700003600, value: 0.3 },
77
+ ],
78
+ coverage: { total: 2, included: 2 },
79
+ }
80
+
81
+ expect(
82
+ getApplicationCostState(current, trend, {
83
+ currentLoading: false,
84
+ trendLoading: false,
85
+ }),
86
+ ).toBe('partial_missing_current')
87
+ })
88
+
89
+ it('treats all tracked workloads scaled to zero as valid zero state', () => {
90
+ const current: OpenCostApplicationCostResponse = {
91
+ available: true,
92
+ totals: {
93
+ hourlyCost: 0,
94
+ cpuCost: 0,
95
+ memoryCost: 0,
96
+ replicas: 0,
97
+ cpuUsageAvailable: false,
98
+ memoryUsageAvailable: false,
99
+ cpuAllocationUse: 0,
100
+ memoryAllocationUse: 0,
101
+ },
102
+ coverage: { total: 1, included: 1 },
103
+ workloads: [
104
+ {
105
+ kind: 'Deployment',
106
+ namespace: 'prod',
107
+ name: 'api',
108
+ available: true,
109
+ scaledToZero: true,
110
+ current: {
111
+ name: 'api',
112
+ kind: 'Deployment',
113
+ hourlyCost: 0,
114
+ cpuCost: 0,
115
+ memoryCost: 0,
116
+ replicas: 0,
117
+ cpuUsageAvailable: false,
118
+ memoryUsageAvailable: false,
119
+ cpuAllocationUse: 0,
120
+ memoryAllocationUse: 0,
121
+ },
122
+ },
123
+ ],
124
+ }
125
+
126
+ expect(
127
+ getApplicationCostState(current, undefined, {
128
+ currentLoading: false,
129
+ trendLoading: false,
130
+ }),
131
+ ).toBe('zero')
132
+ })
133
+
134
+ it('separates query load failures from absent app metrics', () => {
135
+ expect(getApplicationCostState(undefined, undefined, { currentError: true })).toBe('load_error')
136
+ })
137
+
138
+ it('preserves transport error specificity', () => {
139
+ expect(
140
+ getApplicationCostState(undefined, undefined, {
141
+ currentError: new ApiError('denied', 403),
142
+ }),
143
+ ).toBe('access_denied')
144
+ expect(
145
+ getApplicationCostState(undefined, undefined, {
146
+ trendError: new ApiError('missing', 404),
147
+ }),
148
+ ).toBe('not_found')
149
+ })
150
+
151
+ it('queries only steady-state workload kinds', () => {
152
+ const workloads = [
153
+ { kind: 'Deployment', namespace: 'prod', name: 'api' },
154
+ { kind: 'StatefulSet', namespace: 'prod', name: 'db' },
155
+ { kind: 'DaemonSet', namespace: 'ops', name: 'agent' },
156
+ { kind: 'Job', namespace: 'prod', name: 'migrate' },
157
+ { kind: 'CronJob', namespace: 'prod', name: 'cleanup' },
158
+ ] as AppWorkload[]
159
+
160
+ expect(applicationCostWorkloads(workloads).map((workload) => workload.kind)).toEqual([
161
+ 'Deployment',
162
+ 'StatefulSet',
163
+ 'DaemonSet',
164
+ ])
165
+ expect(applicationCostWorkloads(workloads.slice(3))).toEqual([])
166
+ })
167
+
168
+ it('surfaces app workload access and existence failures', () => {
169
+ const current: OpenCostApplicationCostResponse = {
170
+ available: false,
171
+ reason: 'access_denied',
172
+ totals: {
173
+ hourlyCost: 0,
174
+ cpuCost: 0,
175
+ memoryCost: 0,
176
+ replicas: 0,
177
+ cpuUsageAvailable: false,
178
+ memoryUsageAvailable: false,
179
+ cpuAllocationUse: 0,
180
+ memoryAllocationUse: 0,
181
+ },
182
+ coverage: {
183
+ total: 1,
184
+ included: 0,
185
+ unavailable: [
186
+ {
187
+ kind: 'Deployment',
188
+ namespace: 'prod',
189
+ name: 'api',
190
+ reason: 'access_denied',
191
+ },
192
+ ],
193
+ },
194
+ }
195
+
196
+ expect(
197
+ getApplicationCostState(current, undefined, {
198
+ currentLoading: false,
199
+ trendLoading: false,
200
+ }),
201
+ ).toBe('access_denied')
202
+ expect(getApplicationCostState(current, undefined, { trendError: true })).toBe('access_denied')
203
+ })
204
+ })