@skyhook-io/k8s-ui 1.5.13 → 1.6.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 +3 -3
- package/src/components/cluster-switcher/ClusterSwitcher.tsx +2 -3
- package/src/components/dock/BottomDock.tsx +24 -17
- package/src/components/dock/DockContext.tsx +39 -0
- package/src/components/gitops/index.ts +4 -0
- package/src/components/gitops/insights/GitOpsInsightViews.tsx +1456 -0
- package/src/components/gitops/insights/index.ts +6 -0
- package/src/components/gitops/insights/insights-helpers.test.ts +98 -0
- package/src/components/gitops/insights/insights-helpers.ts +99 -0
- package/src/components/gitops/tree/GitOpsTreeGraph.tsx +799 -0
- package/src/components/gitops/tree/index.ts +4 -0
- package/src/components/gitops/tree/tree-helpers.ts +42 -0
- package/src/components/resources/ResourcesView.tsx +71 -18
- package/src/components/resources/index.ts +1 -1
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +1 -1
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +1 -1
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +1 -1
- package/src/components/resources/renderers/PodRenderer.tsx +4 -3
- package/src/components/resources/renderers/SecretRenderer.tsx +4 -10
- package/src/components/shared/EditableYamlView.tsx +28 -17
- package/src/components/shared/ManagedByChip.tsx +45 -0
- package/src/components/shared/index.ts +1 -0
- package/src/components/timeline/TimelineList.tsx +3 -3
- package/src/components/topology/TopologyGraph.tsx +3 -2
- package/src/components/ui/Tooltip.tsx +10 -1
- package/src/components/ui/drawer-components.tsx +1 -1
- package/src/components/workload/ResourceDetailDrawer.tsx +5 -3
- package/src/components/workload/WorkloadView.tsx +66 -0
- package/src/hooks/useKeyboardShortcuts.tsx +3 -2
- package/src/index.ts +3 -0
- package/src/types/core.ts +19 -2
- package/src/types/gitops-insights.ts +193 -0
- package/src/types/gitops-tree.ts +57 -0
- package/src/types/index.ts +2 -0
- package/src/utils/badge-colors.ts +6 -1
- package/src/utils/format.ts +28 -0
- package/src/utils/gitops-owner.test.ts +136 -0
- package/src/utils/gitops-owner.ts +92 -0
- package/src/utils/gitops-route.test.ts +78 -0
- package/src/utils/gitops-route.ts +104 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/navigation.ts +14 -0
- package/src/utils/resource-hierarchy.ts +47 -3
- package/src/utils/yaml.test.ts +101 -0
- package/src/utils/yaml.ts +26 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { parse as yamlParse } from 'yaml'
|
|
3
|
+
import { cleanResourceForYaml, resourceToYaml } from './yaml'
|
|
4
|
+
|
|
5
|
+
function makePod() {
|
|
6
|
+
return {
|
|
7
|
+
apiVersion: 'v1',
|
|
8
|
+
kind: 'Pod',
|
|
9
|
+
metadata: {
|
|
10
|
+
name: 'nginx',
|
|
11
|
+
namespace: 'default',
|
|
12
|
+
uid: 'abcd-1234',
|
|
13
|
+
resourceVersion: '99999',
|
|
14
|
+
creationTimestamp: '2026-01-01T00:00:00Z',
|
|
15
|
+
generation: 1,
|
|
16
|
+
managedFields: [{ manager: 'kubectl', operation: 'Update' }],
|
|
17
|
+
labels: { app: 'nginx' },
|
|
18
|
+
annotations: { 'kubectl.kubernetes.io/last-applied-configuration': '{}' },
|
|
19
|
+
ownerReferences: [{ kind: 'ReplicaSet', name: 'nginx-rs', uid: 'rs-uid' }],
|
|
20
|
+
},
|
|
21
|
+
spec: {
|
|
22
|
+
containers: [{ name: 'nginx', image: 'nginx:1.25' }],
|
|
23
|
+
},
|
|
24
|
+
status: {
|
|
25
|
+
phase: 'Running',
|
|
26
|
+
podIP: '10.0.0.1',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('cleanResourceForYaml', () => {
|
|
32
|
+
it('strips status', () => {
|
|
33
|
+
const cleaned = cleanResourceForYaml(makePod())
|
|
34
|
+
expect(cleaned.status).toBeUndefined()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('strips all 5 server-generated metadata fields', () => {
|
|
38
|
+
const cleaned = cleanResourceForYaml(makePod())
|
|
39
|
+
expect(cleaned.metadata.uid).toBeUndefined()
|
|
40
|
+
expect(cleaned.metadata.resourceVersion).toBeUndefined()
|
|
41
|
+
expect(cleaned.metadata.creationTimestamp).toBeUndefined()
|
|
42
|
+
expect(cleaned.metadata.generation).toBeUndefined()
|
|
43
|
+
expect(cleaned.metadata.managedFields).toBeUndefined()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('preserves user metadata', () => {
|
|
47
|
+
const cleaned = cleanResourceForYaml(makePod())
|
|
48
|
+
expect(cleaned.metadata.name).toBe('nginx')
|
|
49
|
+
expect(cleaned.metadata.namespace).toBe('default')
|
|
50
|
+
expect(cleaned.metadata.labels).toEqual({ app: 'nginx' })
|
|
51
|
+
expect(cleaned.metadata.annotations).toEqual({
|
|
52
|
+
'kubectl.kubernetes.io/last-applied-configuration': '{}',
|
|
53
|
+
})
|
|
54
|
+
expect(cleaned.metadata.ownerReferences).toHaveLength(1)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('preserves spec', () => {
|
|
58
|
+
const cleaned = cleanResourceForYaml(makePod())
|
|
59
|
+
expect(cleaned.spec).toEqual({
|
|
60
|
+
containers: [{ name: 'nginx', image: 'nginx:1.25' }],
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('does not mutate the input', () => {
|
|
65
|
+
const input = makePod()
|
|
66
|
+
cleanResourceForYaml(input)
|
|
67
|
+
expect(input.status).toBeDefined()
|
|
68
|
+
expect(input.metadata.uid).toBe('abcd-1234')
|
|
69
|
+
expect(input.metadata.managedFields).toHaveLength(1)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('returns null/undefined/primitives unchanged', () => {
|
|
73
|
+
expect(cleanResourceForYaml(null)).toBeNull()
|
|
74
|
+
expect(cleanResourceForYaml(undefined)).toBeUndefined()
|
|
75
|
+
expect(cleanResourceForYaml('not an object' as any)).toBe('not an object')
|
|
76
|
+
expect(cleanResourceForYaml(42 as any)).toBe(42)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('handles object with no metadata field', () => {
|
|
80
|
+
const cleaned = cleanResourceForYaml({ kind: 'Pod', spec: {} })
|
|
81
|
+
expect(cleaned).toEqual({ kind: 'Pod', spec: {} })
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('handles metadata present but null', () => {
|
|
85
|
+
const cleaned = cleanResourceForYaml({ kind: 'Pod', metadata: null, spec: {} })
|
|
86
|
+
expect(cleaned).toEqual({ kind: 'Pod', metadata: null, spec: {} })
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('resourceToYaml', () => {
|
|
91
|
+
it('returns empty string for null/undefined', () => {
|
|
92
|
+
expect(resourceToYaml(null)).toBe('')
|
|
93
|
+
expect(resourceToYaml(undefined)).toBe('')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('round-trips through yaml.parse to the cleaned object', () => {
|
|
97
|
+
const cleaned = cleanResourceForYaml(makePod())
|
|
98
|
+
const yaml = resourceToYaml(makePod())
|
|
99
|
+
expect(yamlParse(yaml)).toEqual(cleaned)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { stringify as yamlStringify } from 'yaml'
|
|
2
|
+
|
|
3
|
+
const SERVER_GENERATED_METADATA = [
|
|
4
|
+
'managedFields',
|
|
5
|
+
'resourceVersion',
|
|
6
|
+
'uid',
|
|
7
|
+
'creationTimestamp',
|
|
8
|
+
'generation',
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
export function cleanResourceForYaml<T = any>(data: T): T {
|
|
12
|
+
if (!data || typeof data !== 'object') return data
|
|
13
|
+
const cleaned = structuredClone(data) as any
|
|
14
|
+
delete cleaned.status
|
|
15
|
+
if (cleaned.metadata && typeof cleaned.metadata === 'object') {
|
|
16
|
+
for (const field of SERVER_GENERATED_METADATA) {
|
|
17
|
+
delete cleaned.metadata[field]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return cleaned
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resourceToYaml(data: any): string {
|
|
24
|
+
if (!data) return ''
|
|
25
|
+
return yamlStringify(cleanResourceForYaml(data), { lineWidth: 0, indent: 2 })
|
|
26
|
+
}
|