@skyhook-io/k8s-ui 1.15.0 → 1.16.1
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/audit/AuditFindingsTable.tsx +1 -1
- package/src/components/checks/ChecksView.tsx +8 -8
- package/src/components/gitops/GitOpsDetailLayout.tsx +3 -1
- package/src/components/gitops/GitOpsHealthSourceNotice.tsx +12 -2
- package/src/components/gitops/index.ts +1 -0
- package/src/components/gitops/insights/GitOpsInsightViews.tsx +4 -1
- package/src/components/issues/IssuesView.tsx +2 -2
- package/src/components/logs/LogCore.tsx +131 -97
- package/src/components/logs/StructuredLogLine.tsx +18 -28
- package/src/components/logs/log-palette.ts +4 -0
- package/src/components/logs/useLogBuffer.ts +8 -39
- package/src/components/logs/useLogSearch.ts +52 -24
- package/src/components/namespace-switcher/NamespacePicker.test.tsx +140 -0
- package/src/components/namespace-switcher/NamespacePicker.tsx +26 -20
- package/src/components/resources/renderers/PodRenderer.test.tsx +15 -0
- package/src/components/resources/renderers/PodRenderer.tsx +10 -2
- package/src/components/resources/renderers/PolicySection.test.tsx +42 -0
- package/src/components/resources/renderers/PolicySection.tsx +10 -1
- package/src/components/shared/DrainPlanDialog.test.tsx +10 -0
- package/src/components/shared/DrainPlanDialog.tsx +14 -3
- package/src/components/shared/ResourceActionsBar.tsx +14 -9
- package/src/components/ui/FilterPill.tsx +1 -1
- package/src/components/ui/RadarUpgradeNote.click.test.tsx +24 -0
- package/src/components/ui/RadarUpgradeNote.test.tsx +66 -0
- package/src/components/ui/RadarUpgradeNote.tsx +129 -0
- package/src/components/ui/ResourceBar.tsx +12 -6
- package/src/components/ui/SelectMenu.tsx +62 -19
- package/src/components/ui/index.ts +8 -0
- package/src/components/workload/WorkloadView.tsx +109 -3
- package/src/types/core.ts +25 -4
- package/src/types/fetch-error.ts +33 -0
- package/src/utils/log-format.ts +1 -1
- package/src/utils/log-level.test.ts +269 -0
- package/src/utils/log-level.ts +301 -0
- package/src/utils/resource-hierarchy.test.ts +69 -0
- package/src/utils/resource-hierarchy.ts +87 -52
|
@@ -91,6 +91,11 @@ export interface HierarchyOptions {
|
|
|
91
91
|
topology?: Topology
|
|
92
92
|
/** If provided, returns only the hierarchy rooted at this resource */
|
|
93
93
|
rootResource?: { kind: string; group?: string; namespace: string; name: string }
|
|
94
|
+
/** The events were already selected as rootResource's history (the server's
|
|
95
|
+
* scoped query). Every lane is kept: one the hierarchy can't place in the
|
|
96
|
+
* root's tree — a past run whose parent left no events, a second Service —
|
|
97
|
+
* nests under the returned lane instead of being dropped. */
|
|
98
|
+
eventsScopedToRoot?: boolean
|
|
94
99
|
/** Legacy toggle. Kept for back-compat (WorkloadView): true→'app', false→'owner'
|
|
95
100
|
* when `grouping` is omitted. Prefer `grouping`. */
|
|
96
101
|
groupByApp?: boolean
|
|
@@ -933,66 +938,96 @@ export function buildResourceHierarchy(options: HierarchyOptions): ResourceLane[
|
|
|
933
938
|
|
|
934
939
|
// If rootResource is specified, filter to only include lanes related to that resource
|
|
935
940
|
if (rootResource) {
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return [rootLane]
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// If root resource is a child of another lane, find its parent and return that
|
|
948
|
-
for (const lane of topLevelLanes) {
|
|
949
|
-
if (lane.children?.some(c => c.id === rootLaneId)) {
|
|
950
|
-
return [lane]
|
|
941
|
+
const rootLane = pickRootLane(rootResource, topLevelLanes, laneMap, laneParent, findRoot, resolveId)
|
|
942
|
+
if (options.eventsScopedToRoot) {
|
|
943
|
+
const adopted = topLevelLanes.filter((l) => l !== rootLane && !laneContains(l, rootLane))
|
|
944
|
+
if (adopted.length > 0) {
|
|
945
|
+
rootLane.children = [...(rootLane.children ?? []), ...adopted]
|
|
946
|
+
rootLane.childEventCount = (rootLane.childEventCount ?? 0) + adopted.reduce((n, l) => n + l.events.length, 0)
|
|
947
|
+
sortLaneChildrenDeep(rootLane)
|
|
948
|
+
rootLane.allEventsSorted = subtreeEvents(rootLane)
|
|
951
949
|
}
|
|
952
|
-
// Also check if root is the parent and we're looking at the hierarchy from the parent perspective
|
|
953
950
|
}
|
|
951
|
+
return [rootLane]
|
|
952
|
+
}
|
|
954
953
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
954
|
+
return topLevelLanes
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
function laneContains(lane: ResourceLane, target: ResourceLane, seen = new Set<ResourceLane>()): boolean {
|
|
959
|
+
if (seen.has(lane)) return false
|
|
960
|
+
seen.add(lane)
|
|
961
|
+
return (lane.children ?? []).some((c) => c === target || laneContains(c, target, seen))
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/** The lane a rootResource view shows: the root's own lane, the top-level
|
|
965
|
+
* ancestor it nests under, or a placeholder when it has no events. */
|
|
966
|
+
function pickRootLane(
|
|
967
|
+
rootResource: { kind: string; group?: string; namespace: string; name: string },
|
|
968
|
+
topLevelLanes: ResourceLane[],
|
|
969
|
+
laneMap: Map<string, ResourceLane>,
|
|
970
|
+
laneParent: Map<string, string>,
|
|
971
|
+
findRoot: (id: string) => string,
|
|
972
|
+
resolveId: (resourceKey: string) => string,
|
|
973
|
+
): ResourceLane {
|
|
974
|
+
const rootGroup = canonicalResourceGroup(rootResource.kind, rootResource.group)
|
|
975
|
+
const rootLaneId = rootGroup !== undefined
|
|
976
|
+
? laneId(rootResource.kind, rootGroup, rootResource.namespace, rootResource.name)
|
|
977
|
+
: resolveId(laneResourceKey(rootResource.kind, rootResource.namespace, rootResource.name))
|
|
978
|
+
const rootLane = topLevelLanes.find(l => l.id === rootLaneId)
|
|
979
|
+
|
|
980
|
+
if (rootLane) {
|
|
981
|
+
// Return the root lane with all its children
|
|
982
|
+
return rootLane
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// If root resource is a child of another lane, find its parent and return that
|
|
986
|
+
for (const lane of topLevelLanes) {
|
|
987
|
+
if (lane.children?.some(c => c.id === rootLaneId)) {
|
|
988
|
+
return lane
|
|
975
989
|
}
|
|
990
|
+
// Also check if root is the parent and we're looking at the hierarchy from the parent perspective
|
|
991
|
+
}
|
|
976
992
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
993
|
+
// If not found, check if it's a child somewhere in the hierarchy and return its parent
|
|
994
|
+
const childLane = laneMap.get(rootLaneId)
|
|
995
|
+
if (childLane) {
|
|
996
|
+
const parentId = laneParent.get(rootLaneId)
|
|
997
|
+
if (parentId) {
|
|
998
|
+
const parentLane = topLevelLanes.find(l => l.id === parentId)
|
|
999
|
+
if (parentLane) {
|
|
1000
|
+
return parentLane
|
|
1001
|
+
}
|
|
1002
|
+
// Walk up to find the top-level ancestor
|
|
1003
|
+
const rootAncestorId = findRoot(rootLaneId)
|
|
1004
|
+
const rootAncestor = topLevelLanes.find(l => l.id === rootAncestorId)
|
|
1005
|
+
if (rootAncestor) {
|
|
1006
|
+
return rootAncestor
|
|
1007
|
+
}
|
|
991
1008
|
}
|
|
992
|
-
return
|
|
1009
|
+
// No parent found, return as a standalone lane. Full-subtree roll-up so a
|
|
1010
|
+
// nested chain's grandchildren stay in the aggregate.
|
|
1011
|
+
childLane.allEventsSorted = subtreeEvents(childLane)
|
|
1012
|
+
return childLane
|
|
993
1013
|
}
|
|
994
1014
|
|
|
995
|
-
|
|
1015
|
+
// Resource not found in hierarchy - create a placeholder lane
|
|
1016
|
+
// This ensures the detail view always has something to show
|
|
1017
|
+
const placeholderLane: ResourceLane = {
|
|
1018
|
+
id: rootLaneId,
|
|
1019
|
+
kind: rootResource.kind,
|
|
1020
|
+
group: parseLaneId(rootLaneId)?.group || '',
|
|
1021
|
+
identityResolved: rootGroup !== undefined,
|
|
1022
|
+
namespace: rootResource.namespace,
|
|
1023
|
+
name: rootResource.name,
|
|
1024
|
+
events: [],
|
|
1025
|
+
isWorkload: isWorkloadKind(rootResource.kind),
|
|
1026
|
+
children: [],
|
|
1027
|
+
childEventCount: 0,
|
|
1028
|
+
allEventsSorted: [],
|
|
1029
|
+
}
|
|
1030
|
+
return placeholderLane
|
|
996
1031
|
}
|
|
997
1032
|
|
|
998
1033
|
// -----------------------------------------------------------------------------
|