@64mb/freelens-power 1.4.0 → 1.5.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/README.md +3 -3
- package/out/main/common/resource-search.js +61 -0
- package/out/main/common/resource-search.js.map +1 -0
- package/out/main/common/store/monitoring-preferences-store.js +5 -0
- package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/main/index.js +2 -0
- package/out/main/index.js.map +1 -1
- package/out/main/resource-search-database.js +209 -0
- package/out/main/resource-search-database.js.map +1 -0
- package/out/main/resource-search-worker.js +56 -0
- package/out/main/resource-search-worker.js.map +1 -0
- package/out/main/resource-search.js +155 -0
- package/out/main/resource-search.js.map +1 -0
- package/out/renderer/appearance.js +1 -1
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/gateway-relationships-client.js +107 -0
- package/out/renderer/gateway-relationships-client.js.map +1 -0
- package/out/renderer/preferences/monitoring-preferences.js +10 -0
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +3 -3
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/resource-relationships.css.js +1 -1
- package/out/renderer/resource-relationships.css.js.map +1 -1
- package/out/renderer/resource-relationships.js +46 -8
- package/out/renderer/resource-relationships.js.map +1 -1
- package/out/renderer/resource-search-client.js +116 -0
- package/out/renderer/resource-search-client.js.map +1 -0
- package/out/renderer/resource-search-discovery.js +79 -0
- package/out/renderer/resource-search-discovery.js.map +1 -0
- package/out/renderer/resource-search-model.js +2 -1
- package/out/renderer/resource-search-model.js.map +1 -1
- package/out/renderer/resource-search-routing.js +78 -0
- package/out/renderer/resource-search-routing.js.map +1 -0
- package/out/renderer/resource-search-sync.js +199 -0
- package/out/renderer/resource-search-sync.js.map +1 -0
- package/out/renderer/resource-search.js +279 -54
- package/out/renderer/resource-search.js.map +1 -1
- package/out/renderer/src/common/gateway-relationships.js +122 -0
- package/out/renderer/src/common/gateway-relationships.js.map +1 -0
- package/out/renderer/src/common/resource-relationships.js +8 -0
- package/out/renderer/src/common/resource-relationships.js.map +1 -1
- package/out/renderer/src/common/resource-search.js +6 -0
- package/out/renderer/src/common/resource-search.js.map +1 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
//#region src/common/gateway-relationships.ts
|
|
2
|
+
var GROUP = "gateway.networking.k8s.io";
|
|
3
|
+
function conditions(items, generation, types = ["Accepted", "ResolvedRefs"]) {
|
|
4
|
+
return types.map((type) => {
|
|
5
|
+
const condition = items?.find((item) => item.type === type);
|
|
6
|
+
const stale = generation !== void 0 && condition?.observedGeneration !== generation;
|
|
7
|
+
return `${type}: ${condition ? `${condition.status}${condition.reason ? ` (${condition.reason})` : ""}${stale ? " · stale/unknown generation" : ""}` : "unknown"}`;
|
|
8
|
+
}).join(" · ");
|
|
9
|
+
}
|
|
10
|
+
function grantEvidence(route, backend, inventory) {
|
|
11
|
+
const namespace = backend.namespace ?? route.metadata.namespace;
|
|
12
|
+
if (namespace === route.metadata.namespace) return "same-namespace backend";
|
|
13
|
+
if (!inventory.grants || inventory.grantNamespaces && !inventory.grantNamespaces.includes(namespace ?? "")) return "cross-namespace ReferenceGrant unknown";
|
|
14
|
+
return inventory.grants.some((grant) => grant.metadata.namespace === namespace && grant.spec?.from?.some((from) => from.group === GROUP && from.kind === route.kind && from.namespace === route.metadata.namespace) && grant.spec?.to?.some((to) => to.group === "" && to.kind === "Service" && (!to.name || to.name === backend.name))) ? "cross-namespace ReferenceGrant permits backend" : "cross-namespace backend not permitted by observed ReferenceGrants";
|
|
15
|
+
}
|
|
16
|
+
function attachment(route, gateway, listener, inventory) {
|
|
17
|
+
const kinds = listener.allowedRoutes?.kinds;
|
|
18
|
+
if (kinds && !kinds.some((kind) => (kind.group ?? GROUP) === GROUP && kind.kind === route.kind)) return "listener rejects route kind";
|
|
19
|
+
if (!kinds) {
|
|
20
|
+
if (!{
|
|
21
|
+
HTTP: ["HTTPRoute", "GRPCRoute"],
|
|
22
|
+
HTTPS: ["HTTPRoute", "GRPCRoute"],
|
|
23
|
+
TCP: ["TCPRoute"],
|
|
24
|
+
TLS: ["TLSRoute"],
|
|
25
|
+
UDP: ["UDPRoute"]
|
|
26
|
+
}[listener.protocol]?.includes(route.kind)) return "listener rejects route kind or protocol support unknown";
|
|
27
|
+
}
|
|
28
|
+
const ns = listener.allowedRoutes?.namespaces;
|
|
29
|
+
if (!ns || !ns.from || ns.from === "Same") return route.metadata.namespace === gateway.metadata.namespace ? "allowedRoutes permits namespace" : "allowedRoutes rejects namespace";
|
|
30
|
+
if (ns.from === "All") return "allowedRoutes permits all namespaces";
|
|
31
|
+
if (ns.from !== "Selector") return "allowedRoutes namespace unknown";
|
|
32
|
+
const namespace = inventory.namespaces?.find((item) => item.metadata.name === route.metadata.namespace);
|
|
33
|
+
if (!namespace) return "allowedRoutes namespace selector unknown";
|
|
34
|
+
const labels = namespace.metadata.labels ?? {};
|
|
35
|
+
return Object.entries(ns.selector?.matchLabels ?? {}).every(([key, value]) => labels[key] === value) && (ns.selector?.matchExpressions ?? []).every(({ key, operator, values = [] }) => {
|
|
36
|
+
if (operator === "In") return labels[key] !== void 0 && values.includes(labels[key]);
|
|
37
|
+
if (operator === "NotIn") return labels[key] === void 0 || !values.includes(labels[key]);
|
|
38
|
+
if (operator === "Exists") return labels[key] !== void 0;
|
|
39
|
+
if (operator === "DoesNotExist") return labels[key] === void 0;
|
|
40
|
+
return false;
|
|
41
|
+
}) ? "allowedRoutes namespace selector matches" : "allowedRoutes namespace selector rejects";
|
|
42
|
+
}
|
|
43
|
+
function gatewayRelationships(service, inventory) {
|
|
44
|
+
const nodes = [];
|
|
45
|
+
const edges = [];
|
|
46
|
+
let omitted = 0;
|
|
47
|
+
const add = (object, suffix = "", detail = "", evidence) => {
|
|
48
|
+
const id = `gateway-api/${object.kind}/${object.metadata.namespace ?? ""}/${object.metadata.name}${suffix}`;
|
|
49
|
+
if (!nodes.some((node) => node.id === id)) nodes.push({
|
|
50
|
+
id,
|
|
51
|
+
kind: object.kind,
|
|
52
|
+
name: `${object.metadata.name}${suffix}`,
|
|
53
|
+
role: "upstream",
|
|
54
|
+
tone: "neutral",
|
|
55
|
+
detail,
|
|
56
|
+
evidence,
|
|
57
|
+
selfLink: object.selfLink
|
|
58
|
+
});
|
|
59
|
+
return id;
|
|
60
|
+
};
|
|
61
|
+
const routes = inventory.objects.filter((object) => [
|
|
62
|
+
"HTTPRoute",
|
|
63
|
+
"GRPCRoute",
|
|
64
|
+
"TCPRoute",
|
|
65
|
+
"TLSRoute",
|
|
66
|
+
"UDPRoute"
|
|
67
|
+
].includes(object.kind) && object.spec?.rules?.some((rule) => rule.backendRefs?.some((backend) => (backend.group ?? "") === "" && (backend.kind ?? "Service") === "Service" && backend.name === service.metadata.name && (backend.namespace ?? object.metadata.namespace) === service.metadata.namespace)));
|
|
68
|
+
for (const route of routes.slice(0, 5)) {
|
|
69
|
+
const backends = route.spec?.rules?.flatMap((rule) => rule.backendRefs ?? []).filter((backend) => (backend.group ?? "") === "" && (backend.kind ?? "Service") === "Service" && backend.name === service.metadata.name && (backend.namespace ?? route.metadata.namespace) === service.metadata.namespace) ?? [];
|
|
70
|
+
const summaries = route.spec?.rules?.flatMap((rule) => rule.matches?.map((match) => match.path?.value ?? [match.method?.service, match.method?.method].filter(Boolean).join("/")) ?? []) ?? [];
|
|
71
|
+
const routeId = add(route, "", [
|
|
72
|
+
...route.spec?.hostnames ?? [],
|
|
73
|
+
...summaries,
|
|
74
|
+
...backends.map((backend) => `weight ${backend.weight ?? 1} · ${grantEvidence(route, backend, inventory)}`)
|
|
75
|
+
].join(" · "));
|
|
76
|
+
edges.push({
|
|
77
|
+
from: routeId,
|
|
78
|
+
to: "service",
|
|
79
|
+
label: "backend reference (not reachability)"
|
|
80
|
+
});
|
|
81
|
+
const parents = (route.spec?.parentRefs ?? []).filter((parent) => (parent.group ?? GROUP) === GROUP && (parent.kind ?? "Gateway") === "Gateway");
|
|
82
|
+
omitted += Math.max(0, parents.length - 5);
|
|
83
|
+
for (const parent of parents.slice(0, 5)) {
|
|
84
|
+
const gateway = inventory.objects.find((object) => object.kind === "Gateway" && object.metadata.name === parent.name && object.metadata.namespace === (parent.namespace ?? route.metadata.namespace));
|
|
85
|
+
if (!gateway) continue;
|
|
86
|
+
const status = route.status?.parents?.filter((item) => (item.parentRef.group ?? GROUP) === GROUP && (item.parentRef.kind ?? "Gateway") === "Gateway" && item.parentRef.name === parent.name && (item.parentRef.namespace ?? route.metadata.namespace) === (parent.namespace ?? route.metadata.namespace) && item.parentRef.sectionName === parent.sectionName && item.parentRef.port === parent.port);
|
|
87
|
+
const listeners = (gateway.spec?.listeners ?? []).filter((item) => (!parent.sectionName || parent.sectionName === item.name) && (!parent.port || parent.port === item.port));
|
|
88
|
+
omitted += Math.max(0, listeners.length - 5);
|
|
89
|
+
for (const listener of listeners.slice(0, 5)) {
|
|
90
|
+
const listenerStatus = gateway.status?.listeners?.find((item) => item.name === listener.name);
|
|
91
|
+
const id = add(gateway, `/${listener.name}`, `${listener.protocol}:${listener.port}`, [`Listener ${conditions(listenerStatus?.conditions, gateway.metadata.generation, [
|
|
92
|
+
"Accepted",
|
|
93
|
+
"ResolvedRefs",
|
|
94
|
+
"Programmed"
|
|
95
|
+
])}`, `Gateway ${conditions(gateway.status?.conditions, gateway.metadata.generation, ["Accepted", "Programmed"])}`]);
|
|
96
|
+
edges.push({
|
|
97
|
+
from: id,
|
|
98
|
+
to: routeId,
|
|
99
|
+
label: "parent reference",
|
|
100
|
+
evidence: `${attachment(route, gateway, listener, inventory)} · ${status?.length ? status.map((item) => `${item.controllerName ?? "unknown controller"}: ${conditions(item.conditions, route.metadata.generation)}`).join(" · ") : "parent reference · acceptance unknown"}`
|
|
101
|
+
});
|
|
102
|
+
const gatewayClass = inventory.objects.find((object) => object.kind === "GatewayClass" && object.metadata.name === gateway.spec?.gatewayClassName);
|
|
103
|
+
if (gatewayClass) edges.push({
|
|
104
|
+
from: add(gatewayClass, "", conditions(gatewayClass.status?.conditions, gatewayClass.metadata.generation, ["Accepted"])),
|
|
105
|
+
to: id,
|
|
106
|
+
label: "class reference"
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const order = (kind) => kind === "GatewayClass" ? 0 : kind === "Gateway" ? 1 : 2;
|
|
112
|
+
nodes.sort((a, b) => order(a.kind) - order(b.kind));
|
|
113
|
+
return {
|
|
114
|
+
nodes,
|
|
115
|
+
edges,
|
|
116
|
+
omitted: omitted + Math.max(0, routes.length - 5)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
exports.gatewayRelationships = gatewayRelationships;
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=gateway-relationships.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-relationships.js","names":[],"sources":["../../../../src/common/gateway-relationships.ts"],"sourcesContent":["import type { RelationshipEdge, RelationshipNode } from \"./resource-relationships\";\n\nconst GROUP = \"gateway.networking.k8s.io\";\ninterface Condition {\n type: string;\n status: string;\n reason?: string;\n observedGeneration?: number;\n}\ninterface Reference {\n name: string;\n namespace?: string;\n group?: string;\n kind?: string;\n sectionName?: string;\n port?: number;\n weight?: number;\n}\ninterface Listener {\n name: string;\n port: number;\n protocol: string;\n allowedRoutes?: {\n kinds?: { group?: string; kind: string }[];\n namespaces?: {\n from?: string;\n selector?: {\n matchLabels?: Record<string, string>;\n matchExpressions?: { key: string; operator: string; values?: string[] }[];\n };\n };\n };\n}\nexport interface GatewayObject {\n apiVersion?: string;\n kind: string;\n selfLink?: string;\n metadata: { name?: string; namespace?: string; uid?: string; generation?: number; labels?: Record<string, string> };\n spec?: {\n gatewayClassName?: string;\n listeners?: Listener[];\n parentRefs?: Reference[];\n hostnames?: string[];\n rules?: {\n backendRefs?: Reference[];\n matches?: { path?: { value?: string }; method?: { service?: string; method?: string } }[];\n }[];\n from?: { group: string; kind: string; namespace: string }[];\n to?: { group: string; kind: string; name?: string }[];\n };\n status?: {\n conditions?: Condition[];\n listeners?: { name: string; conditions?: Condition[] }[];\n parents?: { parentRef: Reference; controllerName?: string; conditions?: Condition[] }[];\n };\n}\nexport interface GatewayInventory {\n objects: GatewayObject[];\n grants?: GatewayObject[];\n grantNamespaces?: string[];\n namespaces?: GatewayObject[];\n gaps: string[];\n}\nfunction conditions(items: Condition[] | undefined, generation?: number, types = [\"Accepted\", \"ResolvedRefs\"]) {\n return types\n .map((type) => {\n const condition = items?.find((item) => item.type === type);\n const stale = generation !== undefined && condition?.observedGeneration !== generation;\n return `${type}: ${condition ? `${condition.status}${condition.reason ? ` (${condition.reason})` : \"\"}${stale ? \" · stale/unknown generation\" : \"\"}` : \"unknown\"}`;\n })\n .join(\" · \");\n}\nfunction grantEvidence(route: GatewayObject, backend: Reference, inventory: GatewayInventory) {\n const namespace = backend.namespace ?? route.metadata.namespace;\n if (namespace === route.metadata.namespace) return \"same-namespace backend\";\n if (!inventory.grants || (inventory.grantNamespaces && !inventory.grantNamespaces.includes(namespace ?? \"\")))\n return \"cross-namespace ReferenceGrant unknown\";\n const granted = inventory.grants.some(\n (grant) =>\n grant.metadata.namespace === namespace &&\n grant.spec?.from?.some(\n (from) => from.group === GROUP && from.kind === route.kind && from.namespace === route.metadata.namespace,\n ) &&\n grant.spec?.to?.some((to) => to.group === \"\" && to.kind === \"Service\" && (!to.name || to.name === backend.name)),\n );\n return granted\n ? \"cross-namespace ReferenceGrant permits backend\"\n : \"cross-namespace backend not permitted by observed ReferenceGrants\";\n}\nfunction attachment(route: GatewayObject, gateway: GatewayObject, listener: Listener, inventory: GatewayInventory) {\n const kinds = listener.allowedRoutes?.kinds;\n if (kinds && !kinds.some((kind) => (kind.group ?? GROUP) === GROUP && kind.kind === route.kind))\n return \"listener rejects route kind\";\n if (!kinds) {\n const supported: Record<string, string[]> = {\n HTTP: [\"HTTPRoute\", \"GRPCRoute\"],\n HTTPS: [\"HTTPRoute\", \"GRPCRoute\"],\n TCP: [\"TCPRoute\"],\n TLS: [\"TLSRoute\"],\n UDP: [\"UDPRoute\"],\n };\n if (!supported[listener.protocol]?.includes(route.kind))\n return \"listener rejects route kind or protocol support unknown\";\n }\n const ns = listener.allowedRoutes?.namespaces;\n if (!ns || !ns.from || ns.from === \"Same\")\n return route.metadata.namespace === gateway.metadata.namespace\n ? \"allowedRoutes permits namespace\"\n : \"allowedRoutes rejects namespace\";\n if (ns.from === \"All\") return \"allowedRoutes permits all namespaces\";\n if (ns.from !== \"Selector\") return \"allowedRoutes namespace unknown\";\n const namespace = inventory.namespaces?.find((item) => item.metadata.name === route.metadata.namespace);\n if (!namespace) return \"allowedRoutes namespace selector unknown\";\n const labels = namespace.metadata.labels ?? {};\n const selected =\n Object.entries(ns.selector?.matchLabels ?? {}).every(([key, value]) => labels[key] === value) &&\n (ns.selector?.matchExpressions ?? []).every(({ key, operator, values = [] }) => {\n if (operator === \"In\") return labels[key] !== undefined && values.includes(labels[key]);\n if (operator === \"NotIn\") return labels[key] === undefined || !values.includes(labels[key]);\n if (operator === \"Exists\") return labels[key] !== undefined;\n if (operator === \"DoesNotExist\") return labels[key] === undefined;\n return false;\n });\n return selected ? \"allowedRoutes namespace selector matches\" : \"allowedRoutes namespace selector rejects\";\n}\nexport function gatewayRelationships(\n service: { metadata: { name?: string; namespace?: string } },\n inventory: GatewayInventory,\n) {\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n let omitted = 0;\n const add = (object: GatewayObject, suffix = \"\", detail = \"\", evidence?: string[]) => {\n const id = `gateway-api/${object.kind}/${object.metadata.namespace ?? \"\"}/${object.metadata.name}${suffix}`;\n if (!nodes.some((node) => node.id === id))\n nodes.push({\n id,\n kind: object.kind,\n name: `${object.metadata.name}${suffix}`,\n role: \"upstream\",\n tone: \"neutral\",\n detail,\n evidence,\n selfLink: object.selfLink,\n });\n return id;\n };\n const routes = inventory.objects.filter(\n (object) =>\n [\"HTTPRoute\", \"GRPCRoute\", \"TCPRoute\", \"TLSRoute\", \"UDPRoute\"].includes(object.kind) &&\n object.spec?.rules?.some((rule) =>\n rule.backendRefs?.some(\n (backend) =>\n (backend.group ?? \"\") === \"\" &&\n (backend.kind ?? \"Service\") === \"Service\" &&\n backend.name === service.metadata.name &&\n (backend.namespace ?? object.metadata.namespace) === service.metadata.namespace,\n ),\n ),\n );\n for (const route of routes.slice(0, 5)) {\n const backends =\n route.spec?.rules\n ?.flatMap((rule) => rule.backendRefs ?? [])\n .filter(\n (backend) =>\n (backend.group ?? \"\") === \"\" &&\n (backend.kind ?? \"Service\") === \"Service\" &&\n backend.name === service.metadata.name &&\n (backend.namespace ?? route.metadata.namespace) === service.metadata.namespace,\n ) ?? [];\n const summaries =\n route.spec?.rules?.flatMap(\n (rule) =>\n rule.matches?.map(\n (match) => match.path?.value ?? [match.method?.service, match.method?.method].filter(Boolean).join(\"/\"),\n ) ?? [],\n ) ?? [];\n const routeId = add(\n route,\n \"\",\n [\n ...(route.spec?.hostnames ?? []),\n ...summaries,\n ...backends.map((backend) => `weight ${backend.weight ?? 1} · ${grantEvidence(route, backend, inventory)}`),\n ].join(\" · \"),\n );\n edges.push({ from: routeId, to: \"service\", label: \"backend reference (not reachability)\" });\n const parents = (route.spec?.parentRefs ?? []).filter(\n (parent) => (parent.group ?? GROUP) === GROUP && (parent.kind ?? \"Gateway\") === \"Gateway\",\n );\n omitted += Math.max(0, parents.length - 5);\n for (const parent of parents.slice(0, 5)) {\n const gateway = inventory.objects.find(\n (object) =>\n object.kind === \"Gateway\" &&\n object.metadata.name === parent.name &&\n object.metadata.namespace === (parent.namespace ?? route.metadata.namespace),\n );\n if (!gateway) continue;\n const status = route.status?.parents?.filter(\n (item) =>\n (item.parentRef.group ?? GROUP) === GROUP &&\n (item.parentRef.kind ?? \"Gateway\") === \"Gateway\" &&\n item.parentRef.name === parent.name &&\n (item.parentRef.namespace ?? route.metadata.namespace) === (parent.namespace ?? route.metadata.namespace) &&\n item.parentRef.sectionName === parent.sectionName &&\n item.parentRef.port === parent.port,\n );\n const listeners = (gateway.spec?.listeners ?? []).filter(\n (item) =>\n (!parent.sectionName || parent.sectionName === item.name) && (!parent.port || parent.port === item.port),\n );\n omitted += Math.max(0, listeners.length - 5);\n for (const listener of listeners.slice(0, 5)) {\n const listenerStatus = gateway.status?.listeners?.find((item) => item.name === listener.name);\n const id = add(gateway, `/${listener.name}`, `${listener.protocol}:${listener.port}`, [\n `Listener ${conditions(listenerStatus?.conditions, gateway.metadata.generation, [\"Accepted\", \"ResolvedRefs\", \"Programmed\"])}`,\n `Gateway ${conditions(gateway.status?.conditions, gateway.metadata.generation, [\"Accepted\", \"Programmed\"])}`,\n ]);\n edges.push({\n from: id,\n to: routeId,\n label: \"parent reference\",\n evidence: `${attachment(route, gateway, listener, inventory)} · ${status?.length ? status.map((item) => `${item.controllerName ?? \"unknown controller\"}: ${conditions(item.conditions, route.metadata.generation)}`).join(\" · \") : \"parent reference · acceptance unknown\"}`,\n });\n const gatewayClass = inventory.objects.find(\n (object) => object.kind === \"GatewayClass\" && object.metadata.name === gateway.spec?.gatewayClassName,\n );\n if (gatewayClass)\n edges.push({\n from: add(\n gatewayClass,\n \"\",\n conditions(gatewayClass.status?.conditions, gatewayClass.metadata.generation, [\"Accepted\"]),\n ),\n to: id,\n label: \"class reference\",\n });\n }\n }\n }\n const order = (kind: string) => (kind === \"GatewayClass\" ? 0 : kind === \"Gateway\" ? 1 : 2);\n nodes.sort((a, b) => order(a.kind) - order(b.kind));\n return { nodes, edges, omitted: omitted + Math.max(0, routes.length - 5) };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_service_diagnostics = require("./service-diagnostics.js");
|
|
2
2
|
const require_incident_summary = require("./incident-summary.js");
|
|
3
|
+
const require_gateway_relationships = require("./gateway-relationships.js");
|
|
3
4
|
//#region src/common/resource-relationships.ts
|
|
4
5
|
function relationshipReferencePath(kind, name, namespace) {
|
|
5
6
|
return require_incident_summary.incidentResourcePath({
|
|
@@ -51,6 +52,13 @@ function buildResourceRelationships(service, inventory, focusedPod) {
|
|
|
51
52
|
label: "references backend"
|
|
52
53
|
});
|
|
53
54
|
}
|
|
55
|
+
if (inventory.gateway) {
|
|
56
|
+
const gateway = require_gateway_relationships.gatewayRelationships(service, inventory.gateway);
|
|
57
|
+
nodes.push(...gateway.nodes);
|
|
58
|
+
edges.push(...gateway.edges);
|
|
59
|
+
omitted += gateway.omitted;
|
|
60
|
+
gaps.push(...inventory.gateway.gaps);
|
|
61
|
+
}
|
|
54
62
|
const pods = diagnostics.matchingPods?.filter((pod) => !focusedPod || pod.metadata.name === focusedPod) ?? [];
|
|
55
63
|
omitted += Math.max(0, pods.length - 8);
|
|
56
64
|
for (const pod of pods.slice(0, 8)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-relationships.js","names":[],"sources":["../../../../src/common/resource-relationships.ts"],"sourcesContent":["import { incidentResourcePath } from \"./incident-summary\";\nimport {\n diagnoseService,\n type ServiceDiagnosticObject,\n type ServiceDiagnosticPod,\n type ServiceDiagnosticSlice,\n} from \"./service-diagnostics\";\n\nexport interface RelationshipMetadata {\n uid?: string;\n name?: string;\n namespace?: string;\n ownerReferences?: { uid: string; name?: string; kind?: string; controller?: boolean }[];\n}\nexport interface RelationshipObject {\n kind?: string;\n metadata: RelationshipMetadata;\n selfLink?: string;\n}\nexport interface RelationshipPod extends ServiceDiagnosticPod {\n metadata: ServiceDiagnosticPod[\"metadata\"] & RelationshipMetadata;\n spec?: ServiceDiagnosticPod[\"spec\"] & { nodeName?: string };\n status?: ServiceDiagnosticPod[\"status\"] & { phase?: string };\n selfLink?: string;\n}\nexport interface RelationshipSlice extends ServiceDiagnosticSlice {\n selfLink?: string;\n}\nexport interface RelationshipIngress extends RelationshipObject {\n spec?: {\n defaultBackend?: { service?: { name: string } };\n backend?: { serviceName?: string };\n rules?: { http?: { paths?: { backend?: { service?: { name: string }; serviceName?: string } }[] } }[];\n };\n}\nexport interface RelationshipNode {\n id: string;\n kind: string;\n name: string;\n role: \"upstream\" | \"service\" | \"backend\" | \"placement\";\n detail: string;\n tone: \"neutral\" | \"warning\" | \"healthy\";\n selfLink?: string;\n inferred?: boolean;\n}\nexport interface RelationshipEdge {\n from: string;\n to: string;\n label: string;\n}\nexport interface RelationshipInventory {\n pods?: RelationshipPod[];\n slices?: RelationshipSlice[];\n ingresses?: RelationshipIngress[];\n owners?: RelationshipObject[];\n gaps?: string[];\n}\nexport function relationshipReferencePath(kind: string, name: string, namespace?: string) {\n return incidentResourcePath({ kind, name, namespace });\n}\nexport function buildResourceRelationships(\n service: ServiceDiagnosticObject & { selfLink?: string },\n inventory: RelationshipInventory,\n focusedPod?: string,\n) {\n const external = service.spec?.type === \"ExternalName\";\n const diagnostics = diagnoseService(service, external ? [] : inventory.pods, external ? [] : inventory.slices);\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n const gaps = [...(inventory.gaps ?? [])];\n let omitted = 0;\n const namespace = service.metadata.namespace;\n const add = (node: RelationshipNode) => {\n if (!nodes.some((n) => n.id === node.id)) nodes.push(node);\n return node.id;\n };\n const svc = add({\n id: \"service\",\n kind: \"Service\",\n name: service.metadata.name ?? \"Unnamed\",\n role: \"service\",\n detail: diagnostics.summary,\n tone: \"neutral\",\n selfLink: service.selfLink,\n });\n const upstream =\n inventory.ingresses?.filter(\n (ingress) =>\n ingress.metadata.namespace === namespace &&\n [\n ingress.spec?.defaultBackend?.service?.name,\n ingress.spec?.backend?.serviceName,\n ...(ingress.spec?.rules ?? []).flatMap((rule) =>\n (rule.http?.paths ?? []).map((path) => path.backend?.service?.name ?? path.backend?.serviceName),\n ),\n ].includes(service.metadata.name),\n ) ?? [];\n omitted += Math.max(0, upstream.length - 5);\n for (const ingress of upstream.slice(0, 5)) {\n const id = add({\n id: `ingress/${ingress.metadata.uid ?? ingress.metadata.name}`,\n kind: \"Ingress\",\n name: ingress.metadata.name ?? \"Unnamed\",\n role: \"upstream\",\n detail: \"Backend Service reference\",\n tone: \"neutral\",\n selfLink: ingress.selfLink,\n });\n edges.push({ from: id, to: svc, label: \"references backend\" });\n }\n const pods = diagnostics.matchingPods?.filter((pod) => !focusedPod || pod.metadata.name === focusedPod) ?? [];\n omitted += Math.max(0, pods.length - 8);\n for (const pod of pods.slice(0, 8)) {\n const ready = pod.status?.conditions?.find((c) => c.type === \"Ready\");\n const id = add({\n id: `pod/${pod.metadata.uid ?? pod.metadata.name}`,\n kind: \"Pod\",\n name: pod.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail:\n pod.status?.phase === \"Succeeded\"\n ? \"Completed\"\n : pod.status?.phase === \"Failed\"\n ? \"Failed\"\n : pod.metadata.deletionTimestamp\n ? \"Terminating\"\n : pod.status?.phase === \"Pending\"\n ? \"Pending\"\n : ready\n ? `Ready: ${ready.status}`\n : \"Readiness unknown\",\n tone:\n pod.status?.phase === \"Succeeded\"\n ? \"healthy\"\n : pod.status?.phase === \"Failed\" ||\n pod.status?.phase === \"Pending\" ||\n pod.metadata.deletionTimestamp ||\n ready?.status === \"False\"\n ? \"warning\"\n : ready?.status === \"True\"\n ? \"healthy\"\n : \"neutral\",\n selfLink: pod.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"selector matches\" });\n if (pod.spec?.nodeName) {\n const node = add({\n id: `node/${pod.spec.nodeName}`,\n kind: \"Node\",\n name: pod.spec.nodeName,\n role: \"placement\",\n detail: \"Assignment reference; node health not queried\",\n tone: \"neutral\",\n selfLink: relationshipReferencePath(\"Node\", pod.spec.nodeName),\n inferred: true,\n });\n edges.push({ from: id, to: node, label: \"assigned to\" });\n }\n let child = id;\n let refs = pod.metadata.ownerReferences;\n const visited = new Set<string>();\n for (let depth = 0; depth < 4; depth++) {\n const ref = refs?.find((r) => r.controller) ?? (refs?.length === 1 ? refs[0] : undefined);\n if (!ref || visited.has(ref.uid)) break;\n visited.add(ref.uid);\n const owner = inventory.owners?.find((o) => o.metadata.uid === ref.uid && o.metadata.namespace === namespace);\n const ownerId = add({\n id: `owner/${ref.uid}`,\n kind: ref.kind ?? \"Owner\",\n name: ref.name ?? \"Unnamed\",\n role: \"placement\",\n detail: owner ? \"Controller object observed\" : \"Owner reference only; object not observed\",\n tone: \"neutral\",\n selfLink:\n owner?.selfLink ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\", namespace) ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\"),\n inferred: !owner,\n });\n edges.push({ from: child, to: ownerId, label: \"owned by\" });\n child = ownerId;\n refs = owner?.metadata.ownerReferences;\n }\n }\n const slices = diagnostics.matchingSlices ?? [];\n omitted += Math.max(0, slices.length - 5);\n for (const slice of slices.slice(0, 5)) {\n const ready = slice.endpoints?.filter((e) => e.conditions?.ready !== false && !e.conditions?.terminating).length;\n const id = add({\n id: `slice/${slice.metadata.name}`,\n kind: \"EndpointSlice\",\n name: slice.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail: `${slice.addressType} · ${ready ?? \"unknown\"} ready endpoint entries`,\n tone: ready === 0 ? \"warning\" : \"neutral\",\n selfLink: slice.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"published in\" });\n }\n if (!external && !inventory.pods) gaps.push(\"Pod inventory unavailable; selector relationships are unknown\");\n if (!inventory.slices && !diagnostics.external)\n gaps.push(\"EndpointSlice inventory unavailable; published backends are unknown\");\n if (!inventory.ingresses) gaps.push(\"Ingress inventory unavailable; upstream references are unknown\");\n return {\n nodes,\n edges: [...new Map(edges.map((edge) => [JSON.stringify([edge.from, edge.to, edge.label]), edge])).values()],\n omitted,\n gaps: [...new Set(gaps)],\n diagnostics,\n podOptions: diagnostics.matchingPods?.map((p) => p.metadata.name).filter((name): name is string => !!name) ?? [],\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resource-relationships.js","names":[],"sources":["../../../../src/common/resource-relationships.ts"],"sourcesContent":["import { type GatewayInventory, gatewayRelationships } from \"./gateway-relationships\";\nimport { incidentResourcePath } from \"./incident-summary\";\nimport {\n diagnoseService,\n type ServiceDiagnosticObject,\n type ServiceDiagnosticPod,\n type ServiceDiagnosticSlice,\n} from \"./service-diagnostics\";\n\nexport interface RelationshipMetadata {\n uid?: string;\n name?: string;\n namespace?: string;\n ownerReferences?: { uid: string; name?: string; kind?: string; controller?: boolean }[];\n}\nexport interface RelationshipObject {\n kind?: string;\n metadata: RelationshipMetadata;\n selfLink?: string;\n}\nexport interface RelationshipPod extends ServiceDiagnosticPod {\n metadata: ServiceDiagnosticPod[\"metadata\"] & RelationshipMetadata;\n spec?: ServiceDiagnosticPod[\"spec\"] & { nodeName?: string };\n status?: ServiceDiagnosticPod[\"status\"] & { phase?: string };\n selfLink?: string;\n}\nexport interface RelationshipSlice extends ServiceDiagnosticSlice {\n selfLink?: string;\n}\nexport interface RelationshipIngress extends RelationshipObject {\n spec?: {\n defaultBackend?: { service?: { name: string } };\n backend?: { serviceName?: string };\n rules?: { http?: { paths?: { backend?: { service?: { name: string }; serviceName?: string } }[] } }[];\n };\n}\nexport interface RelationshipNode {\n id: string;\n kind: string;\n name: string;\n role: \"upstream\" | \"service\" | \"backend\" | \"placement\";\n detail: string;\n evidence?: string[];\n tone: \"neutral\" | \"warning\" | \"healthy\";\n selfLink?: string;\n inferred?: boolean;\n}\nexport interface RelationshipEdge {\n from: string;\n to: string;\n label: string;\n evidence?: string;\n}\nexport interface RelationshipInventory {\n pods?: RelationshipPod[];\n slices?: RelationshipSlice[];\n ingresses?: RelationshipIngress[];\n owners?: RelationshipObject[];\n gaps?: string[];\n gateway?: GatewayInventory;\n}\nexport function relationshipReferencePath(kind: string, name: string, namespace?: string) {\n return incidentResourcePath({ kind, name, namespace });\n}\nexport function buildResourceRelationships(\n service: ServiceDiagnosticObject & { selfLink?: string },\n inventory: RelationshipInventory,\n focusedPod?: string,\n) {\n const external = service.spec?.type === \"ExternalName\";\n const diagnostics = diagnoseService(service, external ? [] : inventory.pods, external ? [] : inventory.slices);\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n const gaps = [...(inventory.gaps ?? [])];\n let omitted = 0;\n const namespace = service.metadata.namespace;\n const add = (node: RelationshipNode) => {\n if (!nodes.some((n) => n.id === node.id)) nodes.push(node);\n return node.id;\n };\n const svc = add({\n id: \"service\",\n kind: \"Service\",\n name: service.metadata.name ?? \"Unnamed\",\n role: \"service\",\n detail: diagnostics.summary,\n tone: \"neutral\",\n selfLink: service.selfLink,\n });\n const upstream =\n inventory.ingresses?.filter(\n (ingress) =>\n ingress.metadata.namespace === namespace &&\n [\n ingress.spec?.defaultBackend?.service?.name,\n ingress.spec?.backend?.serviceName,\n ...(ingress.spec?.rules ?? []).flatMap((rule) =>\n (rule.http?.paths ?? []).map((path) => path.backend?.service?.name ?? path.backend?.serviceName),\n ),\n ].includes(service.metadata.name),\n ) ?? [];\n omitted += Math.max(0, upstream.length - 5);\n for (const ingress of upstream.slice(0, 5)) {\n const id = add({\n id: `ingress/${ingress.metadata.uid ?? ingress.metadata.name}`,\n kind: \"Ingress\",\n name: ingress.metadata.name ?? \"Unnamed\",\n role: \"upstream\",\n detail: \"Backend Service reference\",\n tone: \"neutral\",\n selfLink: ingress.selfLink,\n });\n edges.push({ from: id, to: svc, label: \"references backend\" });\n }\n if (inventory.gateway) {\n const gateway = gatewayRelationships(service, inventory.gateway);\n nodes.push(...gateway.nodes);\n edges.push(...gateway.edges);\n omitted += gateway.omitted;\n gaps.push(...inventory.gateway.gaps);\n }\n const pods = diagnostics.matchingPods?.filter((pod) => !focusedPod || pod.metadata.name === focusedPod) ?? [];\n omitted += Math.max(0, pods.length - 8);\n for (const pod of pods.slice(0, 8)) {\n const ready = pod.status?.conditions?.find((c) => c.type === \"Ready\");\n const id = add({\n id: `pod/${pod.metadata.uid ?? pod.metadata.name}`,\n kind: \"Pod\",\n name: pod.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail:\n pod.status?.phase === \"Succeeded\"\n ? \"Completed\"\n : pod.status?.phase === \"Failed\"\n ? \"Failed\"\n : pod.metadata.deletionTimestamp\n ? \"Terminating\"\n : pod.status?.phase === \"Pending\"\n ? \"Pending\"\n : ready\n ? `Ready: ${ready.status}`\n : \"Readiness unknown\",\n tone:\n pod.status?.phase === \"Succeeded\"\n ? \"healthy\"\n : pod.status?.phase === \"Failed\" ||\n pod.status?.phase === \"Pending\" ||\n pod.metadata.deletionTimestamp ||\n ready?.status === \"False\"\n ? \"warning\"\n : ready?.status === \"True\"\n ? \"healthy\"\n : \"neutral\",\n selfLink: pod.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"selector matches\" });\n if (pod.spec?.nodeName) {\n const node = add({\n id: `node/${pod.spec.nodeName}`,\n kind: \"Node\",\n name: pod.spec.nodeName,\n role: \"placement\",\n detail: \"Assignment reference; node health not queried\",\n tone: \"neutral\",\n selfLink: relationshipReferencePath(\"Node\", pod.spec.nodeName),\n inferred: true,\n });\n edges.push({ from: id, to: node, label: \"assigned to\" });\n }\n let child = id;\n let refs = pod.metadata.ownerReferences;\n const visited = new Set<string>();\n for (let depth = 0; depth < 4; depth++) {\n const ref = refs?.find((r) => r.controller) ?? (refs?.length === 1 ? refs[0] : undefined);\n if (!ref || visited.has(ref.uid)) break;\n visited.add(ref.uid);\n const owner = inventory.owners?.find((o) => o.metadata.uid === ref.uid && o.metadata.namespace === namespace);\n const ownerId = add({\n id: `owner/${ref.uid}`,\n kind: ref.kind ?? \"Owner\",\n name: ref.name ?? \"Unnamed\",\n role: \"placement\",\n detail: owner ? \"Controller object observed\" : \"Owner reference only; object not observed\",\n tone: \"neutral\",\n selfLink:\n owner?.selfLink ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\", namespace) ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\"),\n inferred: !owner,\n });\n edges.push({ from: child, to: ownerId, label: \"owned by\" });\n child = ownerId;\n refs = owner?.metadata.ownerReferences;\n }\n }\n const slices = diagnostics.matchingSlices ?? [];\n omitted += Math.max(0, slices.length - 5);\n for (const slice of slices.slice(0, 5)) {\n const ready = slice.endpoints?.filter((e) => e.conditions?.ready !== false && !e.conditions?.terminating).length;\n const id = add({\n id: `slice/${slice.metadata.name}`,\n kind: \"EndpointSlice\",\n name: slice.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail: `${slice.addressType} · ${ready ?? \"unknown\"} ready endpoint entries`,\n tone: ready === 0 ? \"warning\" : \"neutral\",\n selfLink: slice.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"published in\" });\n }\n if (!external && !inventory.pods) gaps.push(\"Pod inventory unavailable; selector relationships are unknown\");\n if (!inventory.slices && !diagnostics.external)\n gaps.push(\"EndpointSlice inventory unavailable; published backends are unknown\");\n if (!inventory.ingresses) gaps.push(\"Ingress inventory unavailable; upstream references are unknown\");\n return {\n nodes,\n edges: [...new Map(edges.map((edge) => [JSON.stringify([edge.from, edge.to, edge.label]), edge])).values()],\n omitted,\n gaps: [...new Set(gaps)],\n diagnostics,\n podOptions: diagnostics.matchingPods?.map((p) => p.metadata.name).filter((name): name is string => !!name) ?? [],\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-search.js","names":[],"sources":["../../../../src/common/resource-search.ts"],"sourcesContent":["export const RESOURCE_SEARCH_CHANNEL = \"freelens-power:resource-search\";\nexport interface SearchDocument {\n kind: string;\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n group: string;\n version: string;\n resource: string;\n uid?: string;\n aliases?: string[];\n fields?: { label: string; text: string }[];\n}\nexport interface SearchQueryResult {\n documents: SearchDocument[];\n total: number;\n hasMore: boolean;\n}\nexport interface SearchIndexStatus {\n count: number;\n bytes: number;\n available: boolean;\n}\nexport type SearchIndexRequest = { clusterId: string; contextId: string } & (\n | { operation: \"begin\"; scope: string; scanId: string }\n | { operation: \"complete\"; scope: string; scanId: string }\n | { operation: \"append\"; scope: string; scanId: string; documents: SearchDocument[] }\n | { operation: \"invalidate\"; scope: string }\n | { operation: \"reconcile\"; resources: string[] }\n | { operation: \"permit\" }\n | { operation: \"query\"; query: string; limit?: number; kind?: string; namespace?: string }\n | { operation: \"status\" }\n | { operation: \"clear\" }\n);\n\nconst identifier = (value: unknown, maximum = 253): value is string =>\n typeof value === \"string\" && value.length > 0 && value.length <= maximum && /^[a-zA-Z0-9_.:-]+$/.test(value);\nconst objectName = (value: unknown): value is string =>\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= 1024 &&\n value !== \".\" &&\n value !== \"..\" &&\n !/[/%\\x00-\\x1f\\x7f]/.test(value);\n\nexport function validSearchPartition(value: unknown): value is { clusterId: string; contextId: string } {\n const request = value as SearchIndexRequest;\n return !!request && identifier(request.clusterId, 128) && identifier(request.contextId, 256);\n}\n\nexport function projectSearchDocuments(input: unknown): SearchDocument[] {\n if (!Array.isArray(input) || input.length > 500) throw new Error(\"Invalid search batch\");\n return input.map((document: SearchDocument) => {\n if (\n !document ||\n !identifier(document.kind) ||\n !objectName(document.name) ||\n !identifier(document.version) ||\n !identifier(document.resource) ||\n !(document.group === \"\" || identifier(document.group)) ||\n !(document.namespace === undefined || identifier(document.namespace)) ||\n !(document.uid === undefined || identifier(document.uid, 128))\n )\n throw new Error(\"Invalid search identity\");\n const path = `${document.group ? `/apis/${encodeURIComponent(document.group)}` : \"/api\"}/${encodeURIComponent(document.version)}/${document.namespace ? `namespaces/${encodeURIComponent(document.namespace)}/` : \"\"}${encodeURIComponent(document.resource)}/${encodeURIComponent(document.name)}`;\n if (document.path !== path) throw new Error(\"Invalid search path\");\n const labels = Object.fromEntries(\n Object.entries(document.labels ?? {})\n .slice(0, 64)\n .filter(([key, value]) => key.length <= 253 && typeof value === \"string\" && value.length <= 256),\n );\n const reviewedCollections = new Set([\n \"/pods\",\n \"/nodes\",\n \"/services\",\n \"/namespaces\",\n \"apps/deployments\",\n \"apps/statefulsets\",\n \"apps/daemonsets\",\n \"apps/replicasets\",\n \"batch/jobs\",\n \"batch/cronjobs\",\n \"networking.k8s.io/ingresses\",\n \"autoscaling/horizontalpodautoscalers\",\n ]);\n const fields = reviewedCollections.has(`${document.group}/${document.resource}`)\n ? (document.fields ?? [])\n .slice(0, 32)\n .flatMap((field) =>\n field && typeof field.label === \"string\" && typeof field.text === \"string\"\n ? [{ label: field.label.slice(0, 64), text: field.text.slice(0, 512) }]\n : [],\n )\n : [];\n let remaining = 4096;\n return {\n kind: document.kind,\n name: document.name,\n namespace: document.namespace,\n labels,\n path,\n group: document.group,\n version: document.version,\n resource: document.resource,\n uid: document.uid,\n aliases: Array.isArray(document.aliases)\n ? document.aliases\n .filter((alias) => typeof alias === \"string\" && /^[a-zA-Z0-9_.:-]{1,128}$/.test(alias))\n .slice(0, 32)\n : [],\n fields: fields.flatMap((field) => {\n const text = field.text.slice(0, Math.max(0, remaining));\n remaining -= text.length;\n return text ? [{ label: field.label, text }] : [];\n }),\n };\n });\n}\n"],"mappings":";AAAA,IAAa,0BAA0B"}
|
|
@@ -6,6 +6,7 @@ var MonitoringPreferencesStore = class extends require_extensions.Common.Store.E
|
|
|
6
6
|
enabled = false;
|
|
7
7
|
diagnosticsVisible = true;
|
|
8
8
|
relationshipsVisible = true;
|
|
9
|
+
resourceSearchEnabled = true;
|
|
9
10
|
hiddenUpstreamClusters = [];
|
|
10
11
|
historyEnabled = false;
|
|
11
12
|
trackedHistory = [];
|
|
@@ -16,6 +17,7 @@ var MonitoringPreferencesStore = class extends require_extensions.Common.Store.E
|
|
|
16
17
|
enabled: false,
|
|
17
18
|
diagnosticsVisible: true,
|
|
18
19
|
relationshipsVisible: true,
|
|
20
|
+
resourceSearchEnabled: true,
|
|
19
21
|
hiddenUpstreamClusters: [],
|
|
20
22
|
historyEnabled: false,
|
|
21
23
|
trackedHistory: []
|
|
@@ -25,6 +27,7 @@ var MonitoringPreferencesStore = class extends require_extensions.Common.Store.E
|
|
|
25
27
|
enabled: require__global_external_mobx.observable,
|
|
26
28
|
diagnosticsVisible: require__global_external_mobx.observable,
|
|
27
29
|
relationshipsVisible: require__global_external_mobx.observable,
|
|
30
|
+
resourceSearchEnabled: require__global_external_mobx.observable,
|
|
28
31
|
hiddenUpstreamClusters: require__global_external_mobx.observable,
|
|
29
32
|
historyEnabled: require__global_external_mobx.observable,
|
|
30
33
|
trackedHistory: require__global_external_mobx.observable,
|
|
@@ -35,6 +38,7 @@ var MonitoringPreferencesStore = class extends require_extensions.Common.Store.E
|
|
|
35
38
|
this.enabled = data?.enabled === true;
|
|
36
39
|
this.diagnosticsVisible = data?.diagnosticsVisible !== false;
|
|
37
40
|
this.relationshipsVisible = data?.relationshipsVisible !== false;
|
|
41
|
+
this.resourceSearchEnabled = data?.resourceSearchEnabled !== false;
|
|
38
42
|
this.hiddenUpstreamClusters = Array.isArray(data?.hiddenUpstreamClusters) ? [...new Set(data.hiddenUpstreamClusters.filter((id) => typeof id === "string" && id.length > 0 && id.length <= 256))].slice(-100) : [];
|
|
39
43
|
this.historyEnabled = data?.historyEnabled === true;
|
|
40
44
|
this.trackedHistory = Array.isArray(data?.trackedHistory) ? data.trackedHistory.filter(require_monitoring_history.validHistoryIdentity).slice(0, 100).map(({ clusterId, scope, uid }) => ({
|
|
@@ -48,6 +52,7 @@ var MonitoringPreferencesStore = class extends require_extensions.Common.Store.E
|
|
|
48
52
|
enabled: this.enabled,
|
|
49
53
|
diagnosticsVisible: this.diagnosticsVisible,
|
|
50
54
|
relationshipsVisible: this.relationshipsVisible,
|
|
55
|
+
resourceSearchEnabled: this.resourceSearchEnabled,
|
|
51
56
|
hiddenUpstreamClusters: [...this.hiddenUpstreamClusters],
|
|
52
57
|
historyEnabled: this.historyEnabled,
|
|
53
58
|
trackedHistory: this.trackedHistory.map(({ clusterId, scope, uid }) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitoring-preferences-store.js","names":[],"sources":["../../../../../src/common/store/monitoring-preferences-store.ts"],"sourcesContent":["import { Common } from \"@freelensapp/extensions\";\nimport { action, makeObservable, observable } from \"mobx\";\nimport { type HistoryIdentity, validHistoryIdentity } from \"../monitoring-history\";\n\nexport class MonitoringPreferencesStore extends Common.Store.ExtensionStore<{\n enabled: boolean;\n diagnosticsVisible: boolean;\n relationshipsVisible: boolean;\n hiddenUpstreamClusters: string[];\n historyEnabled: boolean;\n trackedHistory: HistoryIdentity[];\n}> {\n enabled = false;\n diagnosticsVisible = true;\n relationshipsVisible = true;\n hiddenUpstreamClusters: string[] = [];\n historyEnabled = false;\n trackedHistory: HistoryIdentity[] = [];\n\n constructor() {\n super({\n configName: \"monitoring-preferences\",\n defaults: {\n enabled: false,\n diagnosticsVisible: true,\n relationshipsVisible: true,\n hiddenUpstreamClusters: [],\n historyEnabled: false,\n trackedHistory: [],\n },\n });\n makeObservable(this, {\n enabled: observable,\n diagnosticsVisible: observable,\n relationshipsVisible: observable,\n hiddenUpstreamClusters: observable,\n historyEnabled: observable,\n trackedHistory: observable,\n fromStore: action,\n });\n }\n\n fromStore(data: {\n enabled?: boolean;\n diagnosticsVisible?: boolean;\n relationshipsVisible?: boolean;\n hiddenUpstreamClusters?: string[];\n historyEnabled?: boolean;\n trackedHistory?: HistoryIdentity[];\n }): void {\n this.enabled = data?.enabled === true;\n this.diagnosticsVisible = data?.diagnosticsVisible !== false;\n this.relationshipsVisible = data?.relationshipsVisible !== false;\n this.hiddenUpstreamClusters = Array.isArray(data?.hiddenUpstreamClusters)\n ? [\n ...new Set(\n data.hiddenUpstreamClusters.filter((id) => typeof id === \"string\" && id.length > 0 && id.length <= 256),\n ),\n ].slice(-100)\n : [];\n this.historyEnabled = data?.historyEnabled === true;\n this.trackedHistory = Array.isArray(data?.trackedHistory)\n ? data.trackedHistory\n .filter(validHistoryIdentity)\n .slice(0, 100)\n .map(({ clusterId, scope, uid }) => ({ clusterId, scope, uid }))\n : [];\n }\n\n toJSON(): {\n enabled: boolean;\n diagnosticsVisible: boolean;\n relationshipsVisible: boolean;\n hiddenUpstreamClusters: string[];\n historyEnabled: boolean;\n trackedHistory: HistoryIdentity[];\n } {\n return {\n enabled: this.enabled,\n diagnosticsVisible: this.diagnosticsVisible,\n relationshipsVisible: this.relationshipsVisible,\n hiddenUpstreamClusters: [...this.hiddenUpstreamClusters],\n historyEnabled: this.historyEnabled,\n trackedHistory: this.trackedHistory.map(({ clusterId, scope, uid }) => ({ clusterId, scope, uid })),\n };\n }\n}\n"],"mappings":";;;;AAIA,IAAa,6BAAb,cAAgD,mBAAA,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"monitoring-preferences-store.js","names":[],"sources":["../../../../../src/common/store/monitoring-preferences-store.ts"],"sourcesContent":["import { Common } from \"@freelensapp/extensions\";\nimport { action, makeObservable, observable } from \"mobx\";\nimport { type HistoryIdentity, validHistoryIdentity } from \"../monitoring-history\";\n\nexport class MonitoringPreferencesStore extends Common.Store.ExtensionStore<{\n enabled: boolean;\n diagnosticsVisible: boolean;\n relationshipsVisible: boolean;\n resourceSearchEnabled: boolean;\n hiddenUpstreamClusters: string[];\n historyEnabled: boolean;\n trackedHistory: HistoryIdentity[];\n}> {\n enabled = false;\n diagnosticsVisible = true;\n relationshipsVisible = true;\n resourceSearchEnabled = true;\n hiddenUpstreamClusters: string[] = [];\n historyEnabled = false;\n trackedHistory: HistoryIdentity[] = [];\n\n constructor() {\n super({\n configName: \"monitoring-preferences\",\n defaults: {\n enabled: false,\n diagnosticsVisible: true,\n relationshipsVisible: true,\n resourceSearchEnabled: true,\n hiddenUpstreamClusters: [],\n historyEnabled: false,\n trackedHistory: [],\n },\n });\n makeObservable(this, {\n enabled: observable,\n diagnosticsVisible: observable,\n relationshipsVisible: observable,\n resourceSearchEnabled: observable,\n hiddenUpstreamClusters: observable,\n historyEnabled: observable,\n trackedHistory: observable,\n fromStore: action,\n });\n }\n\n fromStore(data: {\n enabled?: boolean;\n diagnosticsVisible?: boolean;\n relationshipsVisible?: boolean;\n resourceSearchEnabled?: boolean;\n hiddenUpstreamClusters?: string[];\n historyEnabled?: boolean;\n trackedHistory?: HistoryIdentity[];\n }): void {\n this.enabled = data?.enabled === true;\n this.diagnosticsVisible = data?.diagnosticsVisible !== false;\n this.relationshipsVisible = data?.relationshipsVisible !== false;\n this.resourceSearchEnabled = data?.resourceSearchEnabled !== false;\n this.hiddenUpstreamClusters = Array.isArray(data?.hiddenUpstreamClusters)\n ? [\n ...new Set(\n data.hiddenUpstreamClusters.filter((id) => typeof id === \"string\" && id.length > 0 && id.length <= 256),\n ),\n ].slice(-100)\n : [];\n this.historyEnabled = data?.historyEnabled === true;\n this.trackedHistory = Array.isArray(data?.trackedHistory)\n ? data.trackedHistory\n .filter(validHistoryIdentity)\n .slice(0, 100)\n .map(({ clusterId, scope, uid }) => ({ clusterId, scope, uid }))\n : [];\n }\n\n toJSON(): {\n enabled: boolean;\n diagnosticsVisible: boolean;\n relationshipsVisible: boolean;\n resourceSearchEnabled: boolean;\n hiddenUpstreamClusters: string[];\n historyEnabled: boolean;\n trackedHistory: HistoryIdentity[];\n } {\n return {\n enabled: this.enabled,\n diagnosticsVisible: this.diagnosticsVisible,\n relationshipsVisible: this.relationshipsVisible,\n resourceSearchEnabled: this.resourceSearchEnabled,\n hiddenUpstreamClusters: [...this.hiddenUpstreamClusters],\n historyEnabled: this.historyEnabled,\n trackedHistory: this.trackedHistory.map(({ clusterId, scope, uid }) => ({ clusterId, scope, uid })),\n };\n }\n}\n"],"mappings":";;;;AAIA,IAAa,6BAAb,cAAgD,mBAAA,OAAO,MAAM,eAQ1D;CACD,UAAU;CACV,qBAAqB;CACrB,uBAAuB;CACvB,wBAAwB;CACxB,yBAAmC,CAAC;CACpC,iBAAiB;CACjB,iBAAoC,CAAC;CAErC,cAAc;EACZ,MAAM;GACJ,YAAY;GACZ,UAAU;IACR,SAAS;IACT,oBAAoB;IACpB,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB,CAAC;IACzB,gBAAgB;IAChB,gBAAgB,CAAC;GACnB;EACF,CAAC;EACD,8BAAA,eAAe,MAAM;GACnB,SAAS,8BAAA;GACT,oBAAoB,8BAAA;GACpB,sBAAsB,8BAAA;GACtB,uBAAuB,8BAAA;GACvB,wBAAwB,8BAAA;GACxB,gBAAgB,8BAAA;GAChB,gBAAgB,8BAAA;GAChB,WAAW,8BAAA;EACb,CAAC;CACH;CAEA,UAAU,MAQD;EACP,KAAK,UAAU,MAAM,YAAY;EACjC,KAAK,qBAAqB,MAAM,uBAAuB;EACvD,KAAK,uBAAuB,MAAM,yBAAyB;EAC3D,KAAK,wBAAwB,MAAM,0BAA0B;EAC7D,KAAK,yBAAyB,MAAM,QAAQ,MAAM,sBAAsB,IACpE,CACE,GAAG,IAAI,IACL,KAAK,uBAAuB,QAAQ,OAAO,OAAO,OAAO,YAAY,GAAG,SAAS,KAAK,GAAG,UAAU,GAAG,CACxG,CACF,CAAC,CAAC,MAAM,IAAI,IACZ,CAAC;EACL,KAAK,iBAAiB,MAAM,mBAAmB;EAC/C,KAAK,iBAAiB,MAAM,QAAQ,MAAM,cAAc,IACpD,KAAK,eACF,OAAO,2BAAA,oBAAoB,CAAC,CAC5B,MAAM,GAAG,GAAG,CAAC,CACb,KAAK,EAAE,WAAW,OAAO,WAAW;GAAE;GAAW;GAAO;EAAI,EAAE,IACjE,CAAC;CACP;CAEA,SAQE;EACA,OAAO;GACL,SAAS,KAAK;GACd,oBAAoB,KAAK;GACzB,sBAAsB,KAAK;GAC3B,uBAAuB,KAAK;GAC5B,wBAAwB,CAAC,GAAG,KAAK,sBAAsB;GACvD,gBAAgB,KAAK;GACrB,gBAAgB,KAAK,eAAe,KAAK,EAAE,WAAW,OAAO,WAAW;IAAE;IAAW;IAAO;GAAI,EAAE;EACpG;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@64mb/freelens-power",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Power tools for Freelens Kubernetes IDE",
|
|
5
5
|
"main": "out/main/index.js",
|
|
6
6
|
"renderer": "out/renderer/index.js",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"@babel/plugin-proposal-decorators": "^8.0.2",
|
|
24
24
|
"@electron-toolkit/tsconfig": "^2.0.0",
|
|
25
25
|
"@freelensapp/extensions": "^1.10.2",
|
|
26
|
+
"@playwright/test": "1.64.1",
|
|
26
27
|
"@testing-library/react": "^12.1.5",
|
|
27
28
|
"@types/node": "~24.12.4",
|
|
28
29
|
"@types/react": "^17.0.93",
|
|
@@ -77,6 +78,9 @@
|
|
|
77
78
|
"clean:out": "pnpm dlx shx@0.4.0 rm -rf out",
|
|
78
79
|
"clean:tgz": "pnpm dlx shx@0.4.0 rm -f \"*.tgz\"",
|
|
79
80
|
"clean:all": "pnpm \"/^clean:(node_modules|out|tgz)\\$/\"",
|
|
80
|
-
"dev": "electron-vite build --watch"
|
|
81
|
+
"dev": "electron-vite build --watch",
|
|
82
|
+
"test:e2e": "playwright test --config e2e/playwright.config.ts",
|
|
83
|
+
"test:e2e:provision": "node e2e/provision.mjs",
|
|
84
|
+
"type:check:e2e": "tsc -p e2e/tsconfig.json"
|
|
81
85
|
}
|
|
82
86
|
}
|