@skyhook-io/k8s-ui 1.5.10 → 1.5.11
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 +1 -1
- package/src/components/dock/BottomDock.tsx +21 -10
- package/src/components/resources/get-pod-phase-display.test.ts +254 -0
- package/src/components/resources/renderers/AWSManagedMachinePoolRenderer.tsx +2 -4
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +3 -2
- package/src/components/resources/renderers/AzureManagedMachinePoolRenderer.tsx +3 -8
- package/src/components/resources/renderers/ChallengeRenderer.tsx +2 -1
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -1
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -1
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -1
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +2 -1
- package/src/components/resources/renderers/GatewayRenderer.tsx +5 -4
- package/src/components/resources/renderers/IngressClassRenderer.tsx +2 -1
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +2 -1
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +3 -2
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +2 -1
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -2
- package/src/components/resources/renderers/OrderRenderer.tsx +2 -1
- package/src/components/resources/renderers/PodRenderer.tsx +23 -2
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +3 -2
- package/src/components/resources/renderers/RolloutRenderer.tsx +2 -1
- package/src/components/resources/renderers/SecretRenderer.tsx +15 -6
- package/src/components/resources/renderers/aws-capi-cells.tsx +2 -4
- package/src/components/resources/renderers/azure-capi-cells.tsx +3 -8
- package/src/components/resources/renderers/istio-cells.tsx +3 -2
- package/src/components/resources/renderers/karpenter-cells.tsx +1 -1
- package/src/components/resources/resource-utils-karpenter.ts +0 -6
- package/src/components/resources/resource-utils.ts +108 -0
- package/src/components/timeline/shared.tsx +5 -6
- package/src/components/ui/Tooltip.tsx +73 -40
- package/src/components/ui/tooltip-position.test.ts +132 -0
- package/src/components/ui/tooltip-position.ts +86 -0
- package/src/components/workload/WorkloadView.tsx +6 -6
- package/src/utils/badge-colors.ts +19 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/validators.test.ts +207 -0
- package/src/utils/validators.ts +178 -0
- package/src/utils/view-transition.test.ts +81 -0
- package/src/utils/view-transition.ts +62 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import { DURATION_DOCK } from '../../utils/animation'
|
|
|
3
3
|
import { X, ChevronDown, ChevronUp, Terminal, FileText, Trash2, Layers, Maximize2, Minimize2, Activity } from 'lucide-react'
|
|
4
4
|
import { clsx } from 'clsx'
|
|
5
5
|
import { useDock, DockTab } from './DockContext'
|
|
6
|
-
import {
|
|
6
|
+
import { useRegisterShortcuts } from '../../hooks/useKeyboardShortcuts'
|
|
7
7
|
|
|
8
8
|
const MIN_HEIGHT = 200
|
|
9
9
|
const DEFAULT_HEIGHT = 400
|
|
@@ -80,15 +80,26 @@ export function BottomDock({ renderTabContent, renderTabHeaderExtra, leftOffset:
|
|
|
80
80
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
81
81
|
}, [isMaximized])
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
useRegisterShortcuts([
|
|
84
|
+
{
|
|
85
|
+
id: 'dock-toggle-backtick',
|
|
86
|
+
keys: 'Ctrl+`',
|
|
87
|
+
description: 'Toggle dock',
|
|
88
|
+
category: 'Dock',
|
|
89
|
+
scope: 'global',
|
|
90
|
+
handler: () => toggleExpanded(),
|
|
91
|
+
enabled: tabs.length > 0,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'dock-toggle-j',
|
|
95
|
+
keys: 'Cmd+J',
|
|
96
|
+
description: 'Toggle dock',
|
|
97
|
+
category: 'Dock',
|
|
98
|
+
scope: 'global',
|
|
99
|
+
handler: () => toggleExpanded(),
|
|
100
|
+
enabled: tabs.length > 0,
|
|
101
|
+
},
|
|
102
|
+
])
|
|
92
103
|
|
|
93
104
|
if (tabs.length === 0) {
|
|
94
105
|
return null
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getPodPhaseDisplay } from './resource-utils'
|
|
3
|
+
|
|
4
|
+
const podRunningHealthy = {
|
|
5
|
+
status: {
|
|
6
|
+
phase: 'Running',
|
|
7
|
+
containerStatuses: [{ name: 'app', ready: true, restartCount: 0 }],
|
|
8
|
+
},
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const podRunningNotReady = {
|
|
12
|
+
status: {
|
|
13
|
+
phase: 'Running',
|
|
14
|
+
containerStatuses: [{ name: 'app', ready: false, restartCount: 0 }],
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const podRunningCycling = {
|
|
19
|
+
status: {
|
|
20
|
+
phase: 'Running',
|
|
21
|
+
containerStatuses: [{ name: 'app', ready: true, restartCount: 6301 }],
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const podRunningNotReadyAndCycling = {
|
|
26
|
+
status: {
|
|
27
|
+
phase: 'Running',
|
|
28
|
+
containerStatuses: [{ name: 'app', ready: false, restartCount: 6301 }],
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const podCrashLooping = {
|
|
33
|
+
status: {
|
|
34
|
+
phase: 'Running',
|
|
35
|
+
containerStatuses: [
|
|
36
|
+
{
|
|
37
|
+
name: 'app',
|
|
38
|
+
ready: false,
|
|
39
|
+
restartCount: 12,
|
|
40
|
+
state: { waiting: { reason: 'CrashLoopBackOff' } },
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const podImagePullBackOff = {
|
|
47
|
+
status: {
|
|
48
|
+
phase: 'Pending',
|
|
49
|
+
containerStatuses: [
|
|
50
|
+
{
|
|
51
|
+
name: 'app',
|
|
52
|
+
ready: false,
|
|
53
|
+
restartCount: 0,
|
|
54
|
+
state: { waiting: { reason: 'ImagePullBackOff' } },
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const podOOMKilled = {
|
|
61
|
+
status: {
|
|
62
|
+
phase: 'Running',
|
|
63
|
+
containerStatuses: [
|
|
64
|
+
{
|
|
65
|
+
name: 'app',
|
|
66
|
+
ready: false,
|
|
67
|
+
restartCount: 3,
|
|
68
|
+
state: { terminated: { reason: 'OOMKilled', exitCode: 137 } },
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const podTerminating = {
|
|
75
|
+
metadata: { deletionTimestamp: '2026-04-29T10:00:00Z' },
|
|
76
|
+
status: {
|
|
77
|
+
phase: 'Running',
|
|
78
|
+
containerStatuses: [{ name: 'app', ready: true, restartCount: 0 }],
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const podErrImagePull = {
|
|
83
|
+
status: {
|
|
84
|
+
phase: 'Pending',
|
|
85
|
+
containerStatuses: [
|
|
86
|
+
{
|
|
87
|
+
name: 'app',
|
|
88
|
+
ready: false,
|
|
89
|
+
restartCount: 0,
|
|
90
|
+
state: { waiting: { reason: 'ErrImagePull' } },
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const podCreateContainerConfigError = {
|
|
97
|
+
status: {
|
|
98
|
+
phase: 'Pending',
|
|
99
|
+
containerStatuses: [
|
|
100
|
+
{
|
|
101
|
+
name: 'app',
|
|
102
|
+
ready: false,
|
|
103
|
+
restartCount: 0,
|
|
104
|
+
state: { waiting: { reason: 'CreateContainerConfigError' } },
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const podMultiContainerPartiallyReady = {
|
|
111
|
+
status: {
|
|
112
|
+
phase: 'Running',
|
|
113
|
+
containerStatuses: [
|
|
114
|
+
{ name: 'app', ready: true, restartCount: 0 },
|
|
115
|
+
{ name: 'sidecar', ready: false, restartCount: 0 },
|
|
116
|
+
{ name: 'proxy', ready: true, restartCount: 0 },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const podMultiContainerCyclingAcrossContainers = {
|
|
122
|
+
status: {
|
|
123
|
+
phase: 'Running',
|
|
124
|
+
containerStatuses: [
|
|
125
|
+
{ name: 'app', ready: true, restartCount: 3 },
|
|
126
|
+
{ name: 'sidecar', ready: true, restartCount: 4 },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const podSucceededWithOOMKilledSidecar = {
|
|
132
|
+
status: {
|
|
133
|
+
phase: 'Succeeded',
|
|
134
|
+
containerStatuses: [
|
|
135
|
+
{ name: 'main', ready: false, restartCount: 0, state: { terminated: { reason: 'Completed', exitCode: 0 } } },
|
|
136
|
+
{ name: 'sidecar', ready: false, restartCount: 1, state: { terminated: { reason: 'OOMKilled', exitCode: 137 } } },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
describe('getPodPhaseDisplay', () => {
|
|
142
|
+
it('returns Running, healthy for a fully-ready pod with no restarts', () => {
|
|
143
|
+
const r = getPodPhaseDisplay(podRunningHealthy)
|
|
144
|
+
expect(r.phase).toBe('Running')
|
|
145
|
+
expect(r.text).toBe('Running')
|
|
146
|
+
expect(r.level).toBe('healthy')
|
|
147
|
+
expect(r.hint).toBeUndefined()
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('downgrades a Running pod that is not ready (the "0/1" case)', () => {
|
|
151
|
+
const r = getPodPhaseDisplay(podRunningNotReady)
|
|
152
|
+
expect(r.phase).toBe('Running')
|
|
153
|
+
expect(r.text).toBe('Running — Not Ready (0/1)')
|
|
154
|
+
expect(r.level).toBe('degraded')
|
|
155
|
+
expect(r.hint).toMatch(/not ready/i)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('downgrades a Running pod with high restarts even if currently ready', () => {
|
|
159
|
+
const r = getPodPhaseDisplay(podRunningCycling)
|
|
160
|
+
expect(r.text).toBe('Running — Restarting (6301 restarts)')
|
|
161
|
+
expect(r.level).toBe('degraded')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('marks a Running + Not Ready + cycling pod as unhealthy', () => {
|
|
165
|
+
const r = getPodPhaseDisplay(podRunningNotReadyAndCycling)
|
|
166
|
+
expect(r.text).toContain('Not Ready (0/1)')
|
|
167
|
+
expect(r.text).toContain('6301 restarts')
|
|
168
|
+
expect(r.level).toBe('unhealthy')
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('always preserves the raw phase field so kubectl users can correlate', () => {
|
|
172
|
+
expect(getPodPhaseDisplay(podRunningNotReady).phase).toBe('Running')
|
|
173
|
+
expect(getPodPhaseDisplay(podCrashLooping).phase).toBe('Running')
|
|
174
|
+
expect(getPodPhaseDisplay(podImagePullBackOff).phase).toBe('Pending')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('flags CrashLoopBackOff as unhealthy with phase preserved', () => {
|
|
178
|
+
const r = getPodPhaseDisplay(podCrashLooping)
|
|
179
|
+
expect(r.text).toBe('Running — CrashLoopBackOff')
|
|
180
|
+
expect(r.level).toBe('unhealthy')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('flags ImagePullBackOff as unhealthy on a Pending pod', () => {
|
|
184
|
+
const r = getPodPhaseDisplay(podImagePullBackOff)
|
|
185
|
+
expect(r.text).toBe('Pending — ImagePullBackOff')
|
|
186
|
+
expect(r.level).toBe('unhealthy')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('flags OOMKilled even when phase is Running', () => {
|
|
190
|
+
const r = getPodPhaseDisplay(podOOMKilled)
|
|
191
|
+
expect(r.text).toBe('Running — OOMKilled')
|
|
192
|
+
expect(r.level).toBe('unhealthy')
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('marks pods with deletionTimestamp as Terminating regardless of phase', () => {
|
|
196
|
+
const r = getPodPhaseDisplay(podTerminating)
|
|
197
|
+
expect(r.text).toBe('Running — Terminating')
|
|
198
|
+
expect(r.level).toBe('degraded')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('falls back to Unknown for missing/unknown phase', () => {
|
|
202
|
+
expect(getPodPhaseDisplay({}).level).toBe('unknown')
|
|
203
|
+
expect(getPodPhaseDisplay({}).phase).toBe('Unknown')
|
|
204
|
+
expect(getPodPhaseDisplay({ status: { phase: 'Banana' } }).level).toBe('unknown')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('handles Succeeded/Pending/Failed phases', () => {
|
|
208
|
+
expect(getPodPhaseDisplay({ status: { phase: 'Succeeded' } }).level).toBe('neutral')
|
|
209
|
+
expect(getPodPhaseDisplay({ status: { phase: 'Pending' } }).level).toBe('degraded')
|
|
210
|
+
expect(getPodPhaseDisplay({ status: { phase: 'Failed' } }).level).toBe('unhealthy')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('flags ErrImagePull as unhealthy', () => {
|
|
214
|
+
const r = getPodPhaseDisplay(podErrImagePull)
|
|
215
|
+
expect(r.text).toBe('Pending — ErrImagePull')
|
|
216
|
+
expect(r.level).toBe('unhealthy')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('flags CreateContainerConfigError as unhealthy', () => {
|
|
220
|
+
const r = getPodPhaseDisplay(podCreateContainerConfigError)
|
|
221
|
+
expect(r.text).toBe('Pending — CreateContainerConfigError')
|
|
222
|
+
expect(r.level).toBe('unhealthy')
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('reports the correct readiness ratio for multi-container pods', () => {
|
|
226
|
+
const r = getPodPhaseDisplay(podMultiContainerPartiallyReady)
|
|
227
|
+
expect(r.text).toBe('Running — Not Ready (2/3)')
|
|
228
|
+
expect(r.level).toBe('degraded')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('sums restartCount across containers when deciding cycling', () => {
|
|
232
|
+
const r = getPodPhaseDisplay(podMultiContainerCyclingAcrossContainers)
|
|
233
|
+
expect(r.text).toBe('Running — Restarting (7 restarts)')
|
|
234
|
+
expect(r.level).toBe('degraded')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('does not flag a Succeeded Job pod as unhealthy when a sidecar OOMed', () => {
|
|
238
|
+
const r = getPodPhaseDisplay(podSucceededWithOOMKilledSidecar)
|
|
239
|
+
expect(r.phase).toBe('Succeeded')
|
|
240
|
+
expect(r.text).toBe('Completed')
|
|
241
|
+
expect(r.level).toBe('neutral')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('does not mistake exactly-RESTART_CYCLING_THRESHOLD restarts for cycling (boundary)', () => {
|
|
245
|
+
const r = getPodPhaseDisplay({
|
|
246
|
+
status: {
|
|
247
|
+
phase: 'Running',
|
|
248
|
+
containerStatuses: [{ name: 'app', ready: true, restartCount: 5 }],
|
|
249
|
+
},
|
|
250
|
+
})
|
|
251
|
+
expect(r.text).toBe('Running')
|
|
252
|
+
expect(r.level).toBe('healthy')
|
|
253
|
+
})
|
|
254
|
+
})
|
|
@@ -3,6 +3,7 @@ import { clsx } from 'clsx'
|
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
|
|
4
4
|
import { getCAPIConditions } from '../resource-utils-capi'
|
|
5
5
|
import { getAWSMMPStatus, getAWSMMPInstanceType, getAWSMMPCapacityType, getAWSMMPAMIType, getAWSMMPNodegroupName, getAWSMMPScaling } from '../resource-utils-aws-capi'
|
|
6
|
+
import { CAPACITY_TYPE_BADGE } from '../../../utils/badge-colors'
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
data: any
|
|
@@ -39,10 +40,7 @@ export function AWSManagedMachinePoolRenderer({ data }: Props) {
|
|
|
39
40
|
<Property label="Instance Type" value={getAWSMMPInstanceType(data)} />
|
|
40
41
|
<Property label="AMI Type" value={getAWSMMPAMIType(data)} />
|
|
41
42
|
<Property label="Capacity Type" value={
|
|
42
|
-
<span className={clsx('badge badge-sm', capacityType === 'spot'
|
|
43
|
-
? 'bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950/50 dark:text-amber-400 dark:border-amber-700/40'
|
|
44
|
-
: 'bg-sky-100 text-sky-700 border-sky-300 dark:bg-sky-950/50 dark:text-sky-400 dark:border-sky-700/40'
|
|
45
|
-
)}>{capacityType === 'onDemand' ? 'On-Demand' : capacityType}</span>
|
|
43
|
+
<span className={clsx('badge badge-sm', capacityType === 'spot' ? CAPACITY_TYPE_BADGE.spot : CAPACITY_TYPE_BADGE.onDemand)}>{capacityType === 'onDemand' ? 'On-Demand' : capacityType}</span>
|
|
46
44
|
} />
|
|
47
45
|
{spec.roleName && <Property label="IAM Role" value={spec.roleName} />}
|
|
48
46
|
</PropertyList>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type ArgoAppStatus,
|
|
10
10
|
type ArgoResource,
|
|
11
11
|
} from '../../../types/gitops'
|
|
12
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
12
13
|
|
|
13
14
|
interface ArgoApplicationRendererProps {
|
|
14
15
|
data: any
|
|
@@ -192,7 +193,7 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
|
|
|
192
193
|
<span
|
|
193
194
|
className={clsx(
|
|
194
195
|
'badge',
|
|
195
|
-
syncPolicy.automated ? 'bg-green-500/20 text-green-400' :
|
|
196
|
+
syncPolicy.automated ? 'bg-green-500/20 text-green-400' : BADGE_INACTIVE
|
|
196
197
|
)}
|
|
197
198
|
>
|
|
198
199
|
{syncPolicy.automated ? 'Enabled' : 'Disabled'}
|
|
@@ -241,7 +242,7 @@ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: Ar
|
|
|
241
242
|
? 'bg-blue-500/20 text-blue-400'
|
|
242
243
|
: operationState.phase === 'Failed' || operationState.phase === 'Error'
|
|
243
244
|
? 'bg-red-500/20 text-red-400'
|
|
244
|
-
:
|
|
245
|
+
: BADGE_INACTIVE
|
|
245
246
|
)}
|
|
246
247
|
>
|
|
247
248
|
{operationState.phase}
|
|
@@ -3,6 +3,7 @@ import { clsx } from 'clsx'
|
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
|
|
4
4
|
import { getCAPIConditions } from '../resource-utils-capi'
|
|
5
5
|
import { getAzureMMPStatus, getAzureMMPSKU, getAzureMMPMode, getAzureMMPOSDiskInfo, getAzureMMPScaling, getAzureMMPScaleSetPriority, getAzureMMPOSType } from '../resource-utils-azure-capi'
|
|
6
|
+
import { CAPACITY_TYPE_BADGE, NODEPOOL_MODE_BADGE } from '../../../utils/badge-colors'
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
data: any
|
|
@@ -34,18 +35,12 @@ export function AzureManagedMachinePoolRenderer({ data }: Props) {
|
|
|
34
35
|
{spec.name && <Property label="Pool Name" value={spec.name} />}
|
|
35
36
|
<Property label="VM Size" value={getAzureMMPSKU(data)} />
|
|
36
37
|
<Property label="Mode" value={
|
|
37
|
-
<span className={clsx('badge badge-sm', mode
|
|
38
|
-
? 'bg-purple-100 text-purple-800 border-purple-300 dark:bg-purple-950/50 dark:text-purple-400 dark:border-purple-700/40'
|
|
39
|
-
: 'bg-sky-100 text-sky-700 border-sky-300 dark:bg-sky-950/50 dark:text-sky-400 dark:border-sky-700/40'
|
|
40
|
-
)}>{mode}</span>
|
|
38
|
+
<span className={clsx('badge badge-sm', NODEPOOL_MODE_BADGE[mode] || NODEPOOL_MODE_BADGE.User)}>{mode}</span>
|
|
41
39
|
} />
|
|
42
40
|
<Property label="OS" value={getAzureMMPOSType(data)} />
|
|
43
41
|
<Property label="OS Disk" value={getAzureMMPOSDiskInfo(data)} />
|
|
44
42
|
<Property label="Priority" value={
|
|
45
|
-
<span className={clsx('badge badge-sm', priority === 'Spot'
|
|
46
|
-
? 'bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950/50 dark:text-amber-400 dark:border-amber-700/40'
|
|
47
|
-
: 'bg-sky-100 text-sky-700 border-sky-300 dark:bg-sky-950/50 dark:text-sky-400 dark:border-sky-700/40'
|
|
48
|
-
)}>{priority}</span>
|
|
43
|
+
<span className={clsx('badge badge-sm', priority === 'Spot' ? CAPACITY_TYPE_BADGE.spot : CAPACITY_TYPE_BADGE.regular)}>{priority}</span>
|
|
49
44
|
} />
|
|
50
45
|
{spec.maxPods != null && <Property label="Max Pods" value={String(spec.maxPods)} />}
|
|
51
46
|
</PropertyList>
|
|
@@ -2,6 +2,7 @@ import { Shield, AlertTriangle, Globe, Key, FileText } from 'lucide-react'
|
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection } from '../../ui/drawer-components'
|
|
4
4
|
import { getSolverType } from './ClusterIssuerRenderer'
|
|
5
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
5
6
|
|
|
6
7
|
function getChallengeStateBadge(state: string): { color: string; text: string } {
|
|
7
8
|
switch (state?.toLowerCase()) {
|
|
@@ -20,7 +21,7 @@ function getChallengeStateBadge(state: string): { color: string; text: string }
|
|
|
20
21
|
case 'errored':
|
|
21
22
|
return { text: 'Errored', color: 'bg-red-500/20 text-red-400' }
|
|
22
23
|
default:
|
|
23
|
-
return { text: state || 'Unknown', color:
|
|
24
|
+
return { text: state || 'Unknown', color: BADGE_INACTIVE }
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -5,6 +5,7 @@ import { Section, PropertyList, Property, AlertBanner } from '../../ui/drawer-co
|
|
|
5
5
|
import { formatAge } from '../resource-utils'
|
|
6
6
|
import { SEVERITY_BADGE_COLORS, SEVERITY_ORDER } from './trivy-shared'
|
|
7
7
|
import { pluralize } from '../../../utils/pluralize'
|
|
8
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
8
9
|
|
|
9
10
|
interface ClusterComplianceReportRendererProps {
|
|
10
11
|
data: any
|
|
@@ -185,7 +186,7 @@ export function ClusterComplianceReportRenderer({ data }: ClusterComplianceRepor
|
|
|
185
186
|
) : (
|
|
186
187
|
<CheckCircle2 className="w-3.5 h-3.5 text-green-400 shrink-0" />
|
|
187
188
|
)}
|
|
188
|
-
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0', SEVERITY_BADGE_COLORS[check.severity] ||
|
|
189
|
+
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0', SEVERITY_BADGE_COLORS[check.severity] || BADGE_INACTIVE)}>
|
|
189
190
|
{check.severity || '-'}
|
|
190
191
|
</span>
|
|
191
192
|
<span className="text-xs text-theme-text-secondary truncate flex-1">{check.name || check.id}</span>
|
|
@@ -4,6 +4,7 @@ import { clsx } from 'clsx'
|
|
|
4
4
|
import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
5
5
|
import { formatAge } from '../resource-utils'
|
|
6
6
|
import { SEVERITY_BADGE_COLORS, SEVERITY_ORDER, TrivyAlertBanner } from './trivy-shared'
|
|
7
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
7
8
|
|
|
8
9
|
interface ConfigAuditReportRendererProps {
|
|
9
10
|
data: any
|
|
@@ -169,7 +170,7 @@ export function ConfigAuditReportRenderer({ data }: ConfigAuditReportRendererPro
|
|
|
169
170
|
) : (
|
|
170
171
|
<XCircle className="w-3.5 h-3.5 text-red-400 shrink-0" />
|
|
171
172
|
)}
|
|
172
|
-
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0', SEVERITY_BADGE_COLORS[group.severity] ||
|
|
173
|
+
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0', SEVERITY_BADGE_COLORS[group.severity] || BADGE_INACTIVE)}>
|
|
173
174
|
{group.severity}
|
|
174
175
|
</span>
|
|
175
176
|
<span className="text-xs text-theme-text-secondary truncate flex-1">{group.title}</span>
|
|
@@ -5,6 +5,7 @@ import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
|
5
5
|
import { formatAge } from '../resource-utils'
|
|
6
6
|
import { SEVERITY_BADGE_COLORS, TrivyAlertBanner, formatTrivyImage } from './trivy-shared'
|
|
7
7
|
import { pluralize } from '../../../utils/pluralize'
|
|
8
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
8
9
|
|
|
9
10
|
interface ExposedSecretReportRendererProps {
|
|
10
11
|
data: any
|
|
@@ -92,7 +93,7 @@ export function ExposedSecretReportRenderer({ data }: ExposedSecretReportRendere
|
|
|
92
93
|
<tr key={`${secret.ruleID || i}-${i}`} className="border-b border-theme-border/50 hover:bg-theme-hover/50">
|
|
93
94
|
<td className="py-1.5 px-1 text-theme-text-secondary font-mono">{secret.ruleID || '-'}</td>
|
|
94
95
|
<td className="py-1.5 px-1">
|
|
95
|
-
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium', SEVERITY_BADGE_COLORS[secret.severity] ||
|
|
96
|
+
<span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium', SEVERITY_BADGE_COLORS[secret.severity] || BADGE_INACTIVE)}>
|
|
96
97
|
{secret.severity || '-'}
|
|
97
98
|
</span>
|
|
98
99
|
</td>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Cpu } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
|
|
4
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
4
5
|
|
|
5
6
|
interface GatewayClassRendererProps {
|
|
6
7
|
data: any
|
|
@@ -40,7 +41,7 @@ export function GatewayClassRenderer({ data }: GatewayClassRendererProps) {
|
|
|
40
41
|
? 'bg-green-500/20 text-green-400'
|
|
41
42
|
: isNotAccepted
|
|
42
43
|
? 'bg-red-500/20 text-red-400'
|
|
43
|
-
:
|
|
44
|
+
: BADGE_INACTIVE
|
|
44
45
|
)}>
|
|
45
46
|
{isAccepted ? 'True' : isNotAccepted ? 'False' : 'Unknown'}
|
|
46
47
|
</span>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Globe, Radio, Lock } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
4
5
|
|
|
5
6
|
const protocolColors: Record<string, string> = {
|
|
6
7
|
HTTP: 'bg-blue-500/20 text-blue-400',
|
|
@@ -73,7 +74,7 @@ export function GatewayRenderer({ data, onNavigate }: GatewayRendererProps) {
|
|
|
73
74
|
? 'bg-green-500/20 text-green-400'
|
|
74
75
|
: isNotAccepted
|
|
75
76
|
? 'bg-red-500/20 text-red-400'
|
|
76
|
-
:
|
|
77
|
+
: BADGE_INACTIVE
|
|
77
78
|
)}>
|
|
78
79
|
{isAccepted ? 'True' : isNotAccepted ? 'False' : 'Unknown'}
|
|
79
80
|
</span>
|
|
@@ -88,7 +89,7 @@ export function GatewayRenderer({ data, onNavigate }: GatewayRendererProps) {
|
|
|
88
89
|
? 'bg-green-500/20 text-green-400'
|
|
89
90
|
: isNotProgrammed
|
|
90
91
|
? 'bg-red-500/20 text-red-400'
|
|
91
|
-
:
|
|
92
|
+
: BADGE_INACTIVE
|
|
92
93
|
)}>
|
|
93
94
|
{isProgrammed ? 'True' : isNotProgrammed ? 'False' : 'Unknown'}
|
|
94
95
|
</span>
|
|
@@ -134,7 +135,7 @@ export function GatewayRenderer({ data, onNavigate }: GatewayRendererProps) {
|
|
|
134
135
|
<span className="text-sm font-medium text-theme-text-primary">{listener.name}</span>
|
|
135
136
|
<span className={clsx(
|
|
136
137
|
'px-1.5 py-0.5 rounded text-[10px] font-medium',
|
|
137
|
-
protocolColors[listener.protocol] ||
|
|
138
|
+
protocolColors[listener.protocol] || BADGE_INACTIVE
|
|
138
139
|
)}>
|
|
139
140
|
{listener.protocol}:{listener.port}
|
|
140
141
|
</span>
|
|
@@ -157,7 +158,7 @@ export function GatewayRenderer({ data, onNavigate }: GatewayRendererProps) {
|
|
|
157
158
|
? 'bg-green-500/20 text-green-400'
|
|
158
159
|
: isListenerNotAccepted
|
|
159
160
|
? 'bg-red-500/20 text-red-400'
|
|
160
|
-
:
|
|
161
|
+
: BADGE_INACTIVE
|
|
161
162
|
)}>
|
|
162
163
|
{isListenerAccepted ? '\u2713' : isListenerNotAccepted ? '\u2717' : '?'}
|
|
163
164
|
</span>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Globe } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property } from '../../ui/drawer-components'
|
|
4
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
4
5
|
|
|
5
6
|
interface IngressClassRendererProps {
|
|
6
7
|
data: any
|
|
@@ -24,7 +25,7 @@ export function IngressClassRenderer({ data }: IngressClassRendererProps) {
|
|
|
24
25
|
'badge',
|
|
25
26
|
isDefault
|
|
26
27
|
? 'bg-green-500/20 text-green-400'
|
|
27
|
-
:
|
|
28
|
+
: BADGE_INACTIVE
|
|
28
29
|
)}>
|
|
29
30
|
{isDefault ? 'Yes' : 'No'}
|
|
30
31
|
</span>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getAuthorizationPolicyRules,
|
|
7
7
|
getAuthorizationPolicySelector,
|
|
8
8
|
} from '../resource-utils-istio'
|
|
9
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
9
10
|
|
|
10
11
|
const actionColors: Record<string, string> = {
|
|
11
12
|
ALLOW: 'bg-green-500/20 text-green-400',
|
|
@@ -52,7 +53,7 @@ export function IstioAuthorizationPolicyRenderer({ data }: IstioAuthorizationPol
|
|
|
52
53
|
<Property label="Action" value={
|
|
53
54
|
<span className={clsx(
|
|
54
55
|
'badge',
|
|
55
|
-
actionColors[action] ||
|
|
56
|
+
actionColors[action] || BADGE_INACTIVE
|
|
56
57
|
)}>
|
|
57
58
|
{action}
|
|
58
59
|
</span>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getIstioGatewayServers,
|
|
7
7
|
getIstioGatewaySelector,
|
|
8
8
|
} from '../resource-utils-istio'
|
|
9
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
9
10
|
|
|
10
11
|
const protocolColors: Record<string, string> = {
|
|
11
12
|
HTTP: 'bg-blue-500/20 text-blue-400',
|
|
@@ -73,7 +74,7 @@ export function IstioGatewayRenderer({ data }: IstioGatewayRendererProps) {
|
|
|
73
74
|
</span>
|
|
74
75
|
<span className={clsx(
|
|
75
76
|
'px-1.5 py-0.5 rounded text-[10px] font-medium',
|
|
76
|
-
protocolColors[protocol] ||
|
|
77
|
+
protocolColors[protocol] || BADGE_INACTIVE
|
|
77
78
|
)}>
|
|
78
79
|
{protocol}:{server.port.number}
|
|
79
80
|
</span>
|
|
@@ -98,7 +99,7 @@ export function IstioGatewayRenderer({ data }: IstioGatewayRendererProps) {
|
|
|
98
99
|
? 'bg-green-500/20 text-green-400'
|
|
99
100
|
: server.tls.mode === 'PASSTHROUGH'
|
|
100
101
|
? 'bg-blue-500/20 text-blue-400'
|
|
101
|
-
:
|
|
102
|
+
: BADGE_INACTIVE
|
|
102
103
|
)}>
|
|
103
104
|
{server.tls.mode || 'SIMPLE'}
|
|
104
105
|
</span>
|
|
@@ -6,12 +6,13 @@ import {
|
|
|
6
6
|
getPeerAuthenticationSelector,
|
|
7
7
|
getPeerAuthenticationPortLevelMtls,
|
|
8
8
|
} from '../resource-utils-istio'
|
|
9
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
9
10
|
|
|
10
11
|
const modeColors: Record<string, string> = {
|
|
11
12
|
STRICT: 'bg-green-500/20 text-green-400',
|
|
12
13
|
PERMISSIVE: 'bg-yellow-500/20 text-yellow-400',
|
|
13
14
|
DISABLE: 'bg-red-500/20 text-red-400',
|
|
14
|
-
UNSET:
|
|
15
|
+
UNSET: BADGE_INACTIVE,
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
interface IstioPeerAuthenticationRendererProps {
|
|
@@ -2,8 +2,8 @@ import { Server, Cpu, Settings } from 'lucide-react'
|
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
4
4
|
import { kindToPlural } from '../../../utils/navigation'
|
|
5
|
+
import { CAPACITY_TYPE_BADGE, BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
5
6
|
import {
|
|
6
|
-
CAPACITY_TYPE_BADGE,
|
|
7
7
|
getNodeClaimStatus,
|
|
8
8
|
getNodeClaimInstanceType,
|
|
9
9
|
getNodeClaimNodeName,
|
|
@@ -188,7 +188,7 @@ export function KarpenterNodeClaimRenderer({ data, onNavigate }: KarpenterNodeCl
|
|
|
188
188
|
isComplete && 'bg-green-500/20 text-green-400',
|
|
189
189
|
isCurrent && 'bg-blue-500/20 text-blue-400',
|
|
190
190
|
isFailed && 'bg-red-500/20 text-red-400',
|
|
191
|
-
isPending && !isCurrent &&
|
|
191
|
+
isPending && !isCurrent && BADGE_INACTIVE
|
|
192
192
|
)}
|
|
193
193
|
>
|
|
194
194
|
{isComplete ? '\u2713' : isFailed ? '\u2717' : isCurrent ? '\u25CF' : '\u25CB'}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ShoppingCart, AlertTriangle, Globe, Shield } from 'lucide-react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { Section, PropertyList, Property, ConditionsSection } from '../../ui/drawer-components'
|
|
4
|
+
import { BADGE_INACTIVE } from '../../../utils/badge-colors'
|
|
4
5
|
|
|
5
6
|
function getOrderStateBadge(state: string): { color: string; text: string } {
|
|
6
7
|
switch (state?.toLowerCase()) {
|
|
@@ -17,7 +18,7 @@ function getOrderStateBadge(state: string): { color: string; text: string } {
|
|
|
17
18
|
case 'errored':
|
|
18
19
|
return { text: 'Errored', color: 'bg-red-500/20 text-red-400' }
|
|
19
20
|
default:
|
|
20
|
-
return { text: state || 'Unknown', color:
|
|
21
|
+
return { text: state || 'Unknown', color: BADGE_INACTIVE }
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|