Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions src/pxl_scripts/dx/shadow_profiles/shadow_profiles.pxl
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@ import px


def learned(start_time: str, clickhouse_dsn: str):
df = px.DataFrame('kubescape_profiles', clickhouse_dsn=clickhouse_dsn, start_time=start_time)
df = df[df.kind == 'shadow']
agg = df.groupby(['namespace', 'name']).agg(
completion=('completion', px.any), status=('status', px.any),
execs=('execs', px.any), opens=('opens', px.any),
egress=('egress', px.any), ingress=('ingress', px.any),
syscalls=('syscalls', px.any), capabilities=('capabilities', px.any))
return agg[['namespace', 'name', 'completion', 'status', 'execs', 'opens',
'egress', 'ingress', 'syscalls', 'capabilities']]
# Reads dx_profiles__latest, a view that resolves the newest generation per
# (namespace, name) over event_time ALONE and only then projects the wide
# columns. Do not go back to kubescape_profiles directly.
#
# This function used to pull the base table and groupby/px.any over execs,
# opens, egress, ingress, syscalls and capabilities. Those columns hold a
# container's entire learned behaviour as newline-joined text, so the query
# materialised every generation of every profile in the window to compute an
# arbitrary "any" of the newest. Measured on edge4: ~1.95 GiB per run against
# a 501 MB / 631k-row table, re-run every 10-40s by the panel's own
# auto-refresh. Concurrent runs crossed the server's 5.40 GiB total budget and
# the ClickHouse container was OOMKilled (exit 137) at 09:39:19Z on
# 2026-09-10. It also cost the KPI series a tick — the single gap in edge4's
# 220 ticks is this outage — so the panel put a hole in the measurement that
# is supposed to evidence completeness.
#
# The failure needs no attacker and no unusual state: one open tab.
df = px.DataFrame('dx_profiles__latest', clickhouse_dsn=clickhouse_dsn,
start_time=start_time)
return df[['namespace', 'name', 'completion', 'status', 'execs', 'opens',
'egress', 'ingress', 'syscalls', 'capabilities']]
6 changes: 3 additions & 3 deletions src/pxl_scripts/dx/shadow_profiles/vis.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"name": "start_time",
"type": "PX_STRING",
"description": "",
"description": "Ignored by this panel. It shows the CURRENT profile set: one row per profile at its newest generation, stamped with the read instant rather than when the profile was learned. Use the learned_at column to see how old each profile is.",
"defaultValue": "-6h"
},
{
Expand Down Expand Up @@ -33,7 +33,7 @@
],
"widgets": [
{
"name": "Shadow profiles",
"name": "Shadow profiles (current set \u2014 the time picker does not filter this panel)",
"position": {
"x": 0,
"y": 0,
Expand All @@ -46,4 +46,4 @@
}
}
]
}
}
Loading