@skyhook-io/k8s-ui 1.14.4 → 1.14.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.
- package/package.json +1 -1
- package/src/components/charts/AreaChart.test.tsx +160 -0
- package/src/components/charts/AreaChart.tsx +191 -39
- package/src/components/charts/PrometheusChartsView.test.tsx +36 -0
- package/src/components/charts/PrometheusChartsView.tsx +193 -76
- package/src/components/charts/SeriesLegend.tsx +8 -3
- package/src/components/charts/annotations.ts +59 -0
- package/src/components/charts/axis.test.ts +32 -0
- package/src/components/charts/axis.ts +60 -0
- package/src/components/charts/colors.test.ts +27 -0
- package/src/components/charts/colors.ts +23 -0
- package/src/components/charts/format.test.ts +19 -0
- package/src/components/charts/format.ts +17 -1
- package/src/components/charts/index.ts +3 -1
- package/src/components/charts/types.ts +12 -0
- package/src/components/resources/HPADiagnosisSummary.test.tsx +178 -0
- package/src/components/resources/HPADiagnosisSummary.tsx +218 -0
- package/src/components/resources/index.ts +7 -0
- package/src/components/resources/renderers/HPARenderer.tsx +4 -57
- package/src/components/resources/renderers/WorkloadRenderer.tsx +4 -20
- package/src/components/topology/K8sResourceNode.test.ts +9 -0
- package/src/components/topology/K8sResourceNode.tsx +56 -5
- package/src/components/ui/Badge.tsx +5 -1
- package/src/components/ui/index.ts +5 -0
- package/src/components/ui/monacoRuntime.ts +14 -0
- package/src/monaco-deep.d.ts +10 -0
- package/src/types/core.ts +23 -10
|
@@ -168,6 +168,34 @@ function getIssueTooltip(issue: string | undefined): React.ReactNode {
|
|
|
168
168
|
);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
// Full port list for the Service subtitle's hover affordance — the subtitle
|
|
172
|
+
// itself only ever shows the first port + a count, so this is the only place
|
|
173
|
+
// a multi-port Service's other ports are visible without opening the detail
|
|
174
|
+
// page. Formatting (hide targetPort when it matches port) mirrors
|
|
175
|
+
// ServicePortCards in ServiceRenderer.tsx so a Service's ports read the same
|
|
176
|
+
// whether glanced at in the graph or opened in the full resource view.
|
|
177
|
+
function servicePortsTooltip(ports: ServicePortEntry[]): React.ReactNode | null {
|
|
178
|
+
if (ports.length < 2) return null;
|
|
179
|
+
return (
|
|
180
|
+
<div className="max-w-xs space-y-0.5">
|
|
181
|
+
{ports.map((p, i) => {
|
|
182
|
+
const target =
|
|
183
|
+
p.targetPort != null && p.targetPort !== String(p.port)
|
|
184
|
+
? ` → ${p.targetPort}`
|
|
185
|
+
: "";
|
|
186
|
+
const proto = p.appProtocol || p.protocol;
|
|
187
|
+
return (
|
|
188
|
+
<div key={i} className="text-[11px]">
|
|
189
|
+
{p.name && <span className="text-theme-text-tertiary">{p.name} </span>}
|
|
190
|
+
<span className="font-medium">{p.port}{target}</span>
|
|
191
|
+
{proto && <span className="text-theme-text-tertiary"> ({proto})</span>}
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
})}
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
171
199
|
// Default dimensions for unknown CRD kinds
|
|
172
200
|
export const DEFAULT_NODE_DIMENSIONS = { width: 260, height: 84 };
|
|
173
201
|
|
|
@@ -322,6 +350,16 @@ const SUMMARY_POD_KINDS = new Set<NodeKind>([
|
|
|
322
350
|
"Service",
|
|
323
351
|
]);
|
|
324
352
|
|
|
353
|
+
// Shape of one entry in a Service node's nodeData.ports (pkg/topology/builder.go's
|
|
354
|
+
// serviceTopologyPorts). name/appProtocol are omitted server-side when unset.
|
|
355
|
+
export interface ServicePortEntry {
|
|
356
|
+
name?: string;
|
|
357
|
+
port: number;
|
|
358
|
+
targetPort?: string;
|
|
359
|
+
protocol?: string;
|
|
360
|
+
appProtocol?: string;
|
|
361
|
+
}
|
|
362
|
+
|
|
325
363
|
export function baseSubtitle(kind: NodeKind, nodeData: Record<string, unknown>): string {
|
|
326
364
|
if (nodeData.deploymentMembership === 'source-only') {
|
|
327
365
|
return `Declared by ${nodeData.deploymentSourceLabel ?? 'deployment source'}`
|
|
@@ -369,8 +407,10 @@ export function baseSubtitle(kind: NodeKind, nodeData: Record<string, unknown>):
|
|
|
369
407
|
return (nodeData.phase as string) || "Unknown";
|
|
370
408
|
case "Service": {
|
|
371
409
|
const svcType = (nodeData.type as string) || "ClusterIP";
|
|
372
|
-
const
|
|
373
|
-
|
|
410
|
+
const ports = (nodeData.ports as ServicePortEntry[] | undefined) || [];
|
|
411
|
+
if (ports.length === 0) return svcType;
|
|
412
|
+
const more = ports.length > 1 ? ` +${ports.length - 1} more` : "";
|
|
413
|
+
return `${svcType} :${ports[0].port}${more}`;
|
|
374
414
|
}
|
|
375
415
|
case "CalicoNetworkPolicy":
|
|
376
416
|
case "CalicoGlobalNetworkPolicy":
|
|
@@ -513,6 +553,9 @@ export const K8sResourceNode = memo(function K8sResourceNode({
|
|
|
513
553
|
const { ownerColorIndex } = ownershipOf(nodeData)
|
|
514
554
|
const hue = ownerColorIndex !== null ? workloadHue(ownerColorIndex) : undefined
|
|
515
555
|
const subtitle = getSubtitle(kind, nodeData)
|
|
556
|
+
const portsTooltip = kind === 'Service'
|
|
557
|
+
? servicePortsTooltip((nodeData.ports as ServicePortEntry[] | undefined) || [])
|
|
558
|
+
: null
|
|
516
559
|
const isInternet = kind === 'Internet'
|
|
517
560
|
const isPodGroup = kind === 'PodGroup'
|
|
518
561
|
const isSmallNode = kind === 'ConfigMap' || kind === 'Secret' || kind === 'ServiceAccount' || kind === 'SealedSecret' || kind === 'ServiceMonitor' || kind === 'PodMonitor' || kind === 'HorizontalPodAutoscaler'
|
|
@@ -730,9 +773,17 @@ export const K8sResourceNode = memo(function K8sResourceNode({
|
|
|
730
773
|
|
|
731
774
|
{/* Subtitle */}
|
|
732
775
|
{subtitle && (
|
|
733
|
-
|
|
734
|
-
{
|
|
735
|
-
|
|
776
|
+
portsTooltip ? (
|
|
777
|
+
<Tooltip content={portsTooltip} position="bottom" wrapperClassName="max-w-full mt-0.5">
|
|
778
|
+
<div className="text-xs text-theme-text-secondary truncate cursor-help">
|
|
779
|
+
{subtitle}
|
|
780
|
+
</div>
|
|
781
|
+
</Tooltip>
|
|
782
|
+
) : (
|
|
783
|
+
<div className="text-xs text-theme-text-secondary truncate mt-0.5">
|
|
784
|
+
{subtitle}
|
|
785
|
+
</div>
|
|
786
|
+
)
|
|
736
787
|
)}
|
|
737
788
|
</div>
|
|
738
789
|
</div>
|
|
@@ -23,7 +23,10 @@ export type BadgeSize = 'sm' | 'default'
|
|
|
23
23
|
// "tell these sibling options apart". The only place sharing is intended.
|
|
24
24
|
// - structural: ports/paths/hosts/weights/names — neutral data fragments, not a
|
|
25
25
|
// status. Its own name so it can diverge from severity-neutral later.
|
|
26
|
-
|
|
26
|
+
// - agent: words an AI agent attached to a Radar fact (evidence roles such
|
|
27
|
+
// as "Cause" / "Rules out"). Muted brand accent on purpose: never a severity
|
|
28
|
+
// hue, so an agent's framing can never be mistaken for a Radar finding.
|
|
29
|
+
export type BadgeTone = 'note' | 'accent1' | 'accent2' | 'accent3' | 'structural' | 'agent'
|
|
27
30
|
|
|
28
31
|
interface BadgeProps {
|
|
29
32
|
/** Severity-based coloring (status badges) */
|
|
@@ -231,6 +234,7 @@ const TONE: Record<BadgeTone, string> = {
|
|
|
231
234
|
accent3: 'bg-teal-100 text-teal-800 border-teal-300 dark:bg-teal-950/50 dark:text-teal-400 dark:border-teal-700/40',
|
|
232
235
|
// neutral data fragment (ports/paths/hosts/names)
|
|
233
236
|
structural: 'bg-theme-elevated text-theme-text-secondary border-theme-border',
|
|
237
|
+
agent: 'bg-accent-muted text-accent-text border-accent/30',
|
|
234
238
|
}
|
|
235
239
|
|
|
236
240
|
// Structure classes
|
|
@@ -33,6 +33,11 @@ export { InClusterConsentDialog } from './InClusterConsentDialog'
|
|
|
33
33
|
export { ToastProvider, useToast, showApiError, showApiSuccess } from './Toast'
|
|
34
34
|
export { CodeViewer } from './CodeViewer'
|
|
35
35
|
export { YamlEditor, YamlDiffEditor } from './YamlEditor'
|
|
36
|
+
// Boots the self-hosted Monaco runtime (bundled editor, local workers) without
|
|
37
|
+
// pulling monaco-editor into the main bundle. Anything that mounts a bare
|
|
38
|
+
// `Editor` must await this first, or @monaco-editor/react reaches for its CDN.
|
|
39
|
+
export const ensureMonacoRuntime = () =>
|
|
40
|
+
import('./monacoRuntime').then(({ ensureMonaco }) => ensureMonaco())
|
|
36
41
|
export type {
|
|
37
42
|
YamlDiagnostic,
|
|
38
43
|
YamlDocumentIdentity,
|
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
import { loader } from '@monaco-editor/react'
|
|
4
4
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
|
|
5
|
+
// Tokenizers only — the set the YAML editor and the pod file viewer hand to
|
|
6
|
+
// Monaco. JSON deliberately absent: its language lives in vs/language/json and
|
|
7
|
+
// spawns a validation worker this runtime does not ship; viewers highlight
|
|
8
|
+
// JSON with the YAML tokenizer, which covers it.
|
|
5
9
|
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'
|
|
10
|
+
import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution'
|
|
11
|
+
import 'monaco-editor/esm/vs/basic-languages/html/html.contribution'
|
|
12
|
+
import 'monaco-editor/esm/vs/basic-languages/css/css.contribution'
|
|
13
|
+
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
|
|
14
|
+
import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
|
|
15
|
+
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution'
|
|
16
|
+
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'
|
|
17
|
+
import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'
|
|
18
|
+
import 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution'
|
|
19
|
+
import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution'
|
|
6
20
|
import 'monaco-editor/esm/vs/editor/contrib/find/browser/findController.js'
|
|
7
21
|
import 'monaco-editor/esm/vs/editor/contrib/folding/browser/folding.js'
|
|
8
22
|
import 'monaco-editor/esm/vs/editor/contrib/format/browser/formatActions.js'
|
package/src/monaco-deep.d.ts
CHANGED
|
@@ -3,3 +3,13 @@ declare module 'monaco-editor/esm/vs/editor/editor.api' {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
declare module 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'
|
|
6
|
+
declare module 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution'
|
|
7
|
+
declare module 'monaco-editor/esm/vs/basic-languages/html/html.contribution'
|
|
8
|
+
declare module 'monaco-editor/esm/vs/basic-languages/css/css.contribution'
|
|
9
|
+
declare module 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
|
|
10
|
+
declare module 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
|
|
11
|
+
declare module 'monaco-editor/esm/vs/basic-languages/python/python.contribution'
|
|
12
|
+
declare module 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'
|
|
13
|
+
declare module 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'
|
|
14
|
+
declare module 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution'
|
|
15
|
+
declare module 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution'
|
package/src/types/core.ts
CHANGED
|
@@ -516,6 +516,9 @@ export interface ContextInfo {
|
|
|
516
516
|
/** Source kubeconfig label (e.g. "kube-cluster-paris"). Set by the backend
|
|
517
517
|
* for contexts loaded through the isolated source registry. */
|
|
518
518
|
source?: string
|
|
519
|
+
/** AWS profile extracted from the exec plugin's --profile arg or AWS_PROFILE
|
|
520
|
+
* env var. Present only for EKS contexts that pin a profile. */
|
|
521
|
+
awsProfile?: string
|
|
519
522
|
}
|
|
520
523
|
|
|
521
524
|
// Namespace
|
|
@@ -683,24 +686,34 @@ export type HPADiagnosisState =
|
|
|
683
686
|
| 'stabilized'
|
|
684
687
|
| 'unknown'
|
|
685
688
|
|
|
686
|
-
export interface
|
|
689
|
+
export interface HPABounds {
|
|
690
|
+
min: number
|
|
691
|
+
max: number
|
|
692
|
+
current: number
|
|
693
|
+
desired: number
|
|
694
|
+
observedGeneration?: number
|
|
695
|
+
generation?: number
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// The subset of an HPA diagnosis that HPADiagnosisSummary presents. Producers
|
|
699
|
+
// that carry a richer or looser envelope (Radar's own HPADiagnosis, the
|
|
700
|
+
// investigation evidence projection) extend this so the presentation rules —
|
|
701
|
+
// state severity, state label, reason redundancy — cannot drift per surface.
|
|
702
|
+
export interface HPADiagnosisView {
|
|
687
703
|
state: HPADiagnosisState
|
|
688
704
|
summary: string
|
|
705
|
+
bounds?: HPABounds
|
|
706
|
+
reasons?: HPAReasonSummary[]
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
export interface HPADiagnosis extends HPADiagnosisView {
|
|
689
710
|
target: {
|
|
690
711
|
apiVersion?: string
|
|
691
712
|
kind?: string
|
|
692
713
|
name?: string
|
|
693
714
|
}
|
|
694
|
-
bounds:
|
|
695
|
-
min: number
|
|
696
|
-
max: number
|
|
697
|
-
current: number
|
|
698
|
-
desired: number
|
|
699
|
-
observedGeneration?: number
|
|
700
|
-
generation?: number
|
|
701
|
-
}
|
|
715
|
+
bounds: HPABounds
|
|
702
716
|
metrics?: HPAMetricSummary[]
|
|
703
|
-
reasons?: HPAReasonSummary[]
|
|
704
717
|
}
|
|
705
718
|
|
|
706
719
|
export interface HPAReasonSummary {
|