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
83 changes: 41 additions & 42 deletions dashboards/google-kubernetes-engine-ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ We also need to create an analytics view for the dashboard to query against. In


```sql
-- 1. Extract raw logs from the maintenance-handler container
-- =========================================================================================
-- GKE MAINTENANCE ANALYTICS VIEW: STATE MACHINE LOGIC
-- This query transforms stateless log entries into stateful maintenance cycles.
-- =========================================================================================

-- 1. EXTRACT RAW LOGS: Initial filtering of the maintenance-handler container logs.
WITH RawLogs AS (
SELECT
timestamp,
Expand All @@ -100,7 +105,7 @@ WITH RawLogs AS (
JSON_VALUE(resource.labels.location) AS zone,
REGEXP_EXTRACT(JSON_VALUE(json_payload.message), r'Handling (?:scheduled )?maintenance event with state: "(.*)"') AS raw_state
FROM
`@__project_id.global._Default._Default`
`@__project_id.global.GKE_MaintenanceData_Bucket._AllLogs`
WHERE
resource.type = 'k8s_container'
AND JSON_VALUE(resource.labels.container_name) = 'maintenance-handler'
Expand All @@ -111,7 +116,7 @@ WITH RawLogs AS (
)
),

-- 2. Determine if the event is a host maintenance termination
-- 2. PRE-PARSE LOGS: Identify Host Maintenance Terminations in the GKE <1.35 case
ParsedLogs AS (
SELECT
timestamp,
Expand All @@ -126,7 +131,7 @@ ParsedLogs AS (
FROM RawLogs
),

-- 3. Parse JSON states into structured fields
-- 3. STRUCTURED DATA: Extracting individual fields from the JSON blob.
ParsedStates AS (
SELECT
timestamp,
Expand All @@ -142,20 +147,10 @@ ParsedStates AS (
FROM ParsedLogs
),

-- 4. Calculate transitions to identify maintenance cycles
-- 4. CALCULATE TRANSITIONS: The core of the state machine.
StateTransitions AS (
SELECT
timestamp,
node,
cluster,
zone,
state_json,
type,
status,
maintenance_start_time,
maintenance_end_time,
is_terminate_event,

*,
(state_json IS NOT NULL OR is_terminate_event) AS upcoming_maintenance,
(state_json IS NOT NULL OR is_terminate_event) AND timestamp >= maintenance_start_time AS maintenance_ongoing,

Expand All @@ -165,17 +160,16 @@ StateTransitions AS (
LAG(maintenance_start_time) OVER(PARTITION BY node ORDER BY timestamp) AS prev_maintenance_start_time,
LAG(maintenance_end_time) OVER(PARTITION BY node ORDER BY timestamp) AS prev_maintenance_end_time,

COALESCE(LAG(state_json IS NOT NULL OR is_terminate_event) OVER(PARTITION BY node ORDER BY timestamp), FALSE) AS prev_upcoming_maintenance,
COALESCE(
LAG(state_json IS NOT NULL OR is_terminate_event) OVER(PARTITION BY node ORDER BY timestamp) AS prev_upcoming_maintenance,
(
LAG(state_json IS NOT NULL OR is_terminate_event) OVER(PARTITION BY node ORDER BY timestamp)
AND LAG(timestamp) OVER(PARTITION BY node ORDER BY timestamp) >= LAG(maintenance_start_time) OVER(PARTITION BY node ORDER BY timestamp),
FALSE
AND LAG(timestamp) OVER(PARTITION BY node ORDER BY timestamp) >= LAG(maintenance_start_time) OVER(PARTITION BY node ORDER BY timestamp)
) AS prev_maintenance_ongoing

FROM ParsedStates
),

-- 5. Generate discrete events from handler state transitions
-- 5. SYNTHETIC EVENT GENERATION: Emits discrete events based on the logic of state transitions.
HandlerGeneratedEvents AS (
SELECT
timestamp,
Expand All @@ -198,15 +192,15 @@ HandlerGeneratedEvents AS (
FROM StateTransitions,
UNNEST([
CASE
WHEN upcoming_maintenance AND NOT maintenance_ongoing AND NOT prev_upcoming_maintenance
WHEN upcoming_maintenance AND NOT maintenance_ongoing AND COALESCE(prev_upcoming_maintenance, FALSE) = FALSE
THEN STRUCT('MaintenanceWindowScheduled' AS reason, 'Maintenance Pending' AS action)
END,
CASE
WHEN maintenance_ongoing AND NOT prev_maintenance_ongoing AND state_json IS NOT NULL
WHEN maintenance_ongoing AND COALESCE(prev_maintenance_ongoing, FALSE) = FALSE AND state_json IS NOT NULL
THEN STRUCT('MaintenanceWindowStarted' AS reason, 'Maintenance Ongoing' AS action)
END,
CASE
WHEN NOT upcoming_maintenance AND prev_upcoming_maintenance
WHEN NOT upcoming_maintenance AND COALESCE(prev_upcoming_maintenance, TRUE) = TRUE
THEN STRUCT('MaintenanceWindowCleared' AS reason, 'Complete' AS action)
END,
CASE
Expand All @@ -218,34 +212,39 @@ HandlerGeneratedEvents AS (
ev IS NOT NULL
),

-- 6. Combine with standard Kubernetes events (Filtering by Component)
-- 6. KUBERNETES NATIVE EVENTS: Pulls events directly from the k8s_node event logs
KubernetesEvents AS (
SELECT
timestamp,
JSON_VALUE(json_payload, '$.reason') AS reason,
JSON_VALUE(json_payload, '$.action') AS action,
JSON_VALUE(resource.labels, '$.node_name') AS node,
JSON_VALUE(resource.labels, '$.cluster_name') AS cluster,
JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/nodepool-name"') AS nodepool,

-- NULLIF on all annotation strings to prevent empty strings ("") from breaking logic
NULLIF(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/nodepool-name"'), '') AS nodepool,
JSON_VALUE(resource.labels, '$.location') AS zone,
JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/type"') AS type,
JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/status"') AS status,
SAFE_CAST(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/maintenance-start-time"') AS TIMESTAMP) AS maintenance_start_time,
SAFE_CAST(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/window-end-time"') AS TIMESTAMP) AS maintenance_end_time
NULLIF(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/type"'), '') AS type,
NULLIF(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/status"'), '') AS status,

-- NULLIF chained to also catch empty strings and Go's '0001-01-01T00:00:00Z' zero-time
SAFE_CAST(NULLIF(NULLIF(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/maintenance-start-time"'), ''), '0001-01-01T00:00:00Z') AS TIMESTAMP) AS maintenance_start_time,
SAFE_CAST(NULLIF(NULLIF(JSON_VALUE(json_payload, '$.metadata.annotations."maintenance.gke.io/window-end-time"'), ''), '0001-01-01T00:00:00Z') AS TIMESTAMP) AS maintenance_end_time

FROM
`@__project_id.global._Default._Default`
`@__project_id.global.GKE_MaintenanceData_Bucket._AllLogs`
WHERE
resource.type = 'k8s_node'
AND (
JSON_VALUE(json_payload, '$.source.component') = 'NodeTerminationHandler'
OR JSON_VALUE(json_payload, '$.reportingComponent') = 'cloud-node-lifecycle-controller'
OR JSON_VALUE(json_payload, '$.reportingComponent') = 'node-controller'
OR JSON_VALUE(json_payload, '$.reason') IN ('CustomerTriggeredMaintenance', 'PodEvictionComplete')
OR JSON_VALUE(json_payload, '$.reason') IN ('CustomerTriggeredMaintenance', 'PodEvictionComplete', 'RegisteredNode', 'DeletingNode', 'RemovingNode', 'NodeNotReady')
)
AND JSON_VALUE(json_payload, '$.reason') NOT IN ('NodeMaintenanceScheduled', 'MaintenanceStarted', 'NoPendingMaintenance')
),

-- 7. Normalize combined events and assign Precedence Ranks
-- 7. NORMALIZE & RANK: Combines both log sources and ranks the status.
normalized_events AS (
SELECT
timestamp, node, reason, action, type, cluster, nodepool, zone, maintenance_start_time, maintenance_end_time,
Expand All @@ -270,14 +269,13 @@ normalized_events AS (
)
),

-- 8. Identify cycle boundaries and assign counts safely ignoring UNKNOWNs
-- 8. BOUNDARY DETECTION: Identifies when one maintenance cycle ends and a new one begins.
flagged_cycles AS (
SELECT
*,
CASE
WHEN status IN ('PENDING', 'ONGOING')
AND COALESCE(
-- skip over UNKNOWN rows and fetch the most recent valid status
LAST_VALUE(CASE WHEN status != 'UNKNOWN' THEN status END IGNORE NULLS)
OVER (PARTITION BY node ORDER BY timestamp ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),
'COMPLETE'
Expand All @@ -287,13 +285,15 @@ flagged_cycles AS (
FROM normalized_events
),

-- 9. ASSIGN CYCLE IDs: Generates a unique integer for each maintenance session per node.
events_with_counts AS (
SELECT
*,
SUM(is_new_cycle) OVER (PARTITION BY node ORDER BY timestamp) AS cycle_count
FROM flagged_cycles
),

-- 10. DEDUPLICATION: Ensures we only keep the latest instance of a reason within a cycle.
distinct_events_ranked AS (
SELECT
e.cycle_count AS CycleCount, e.timestamp AS EventTime, e.reason AS Reason, e.action AS Action,
Expand All @@ -303,13 +303,14 @@ distinct_events_ranked AS (
FROM events_with_counts e
),

-- 11. CLEAN TABLE: Removes duplicates identified in the previous step.
MaintenanceData_Distinct AS (
SELECT * EXCEPT(row_num)
FROM distinct_events_ranked
WHERE row_num = 1
)

-- 9. Final Aggregation & Inter-Cycle Calculation
-- 12. FINAL AGGREGATION: Collapse all events for a cycle into a single summary row.
SELECT
* EXCEPT(prev_valid_end_time, is_valid_cycle),
CASE
Expand All @@ -322,9 +323,8 @@ FROM (
CycleCount,
Node,
Cluster,
Nodepool,

-- FIX: Ignore NULLs so trailing K8s events without metadata don't wipe out the cycle's state
ARRAY_AGG(Nodepool IGNORE NULLS ORDER BY EventTime DESC LIMIT 1)[OFFSET(0)] AS Nodepool,
ARRAY_AGG(Type IGNORE NULLS ORDER BY EventTime DESC LIMIT 1)[OFFSET(0)] AS Type,
ARRAY_AGG(MaintenanceStartTime IGNORE NULLS ORDER BY EventTime DESC LIMIT 1)[OFFSET(0)] AS MaintenanceStartTime,

Expand All @@ -339,13 +339,11 @@ FROM (
ARRAY_AGG(Status ORDER BY status_rank ASC, EventTime DESC LIMIT 1)[OFFSET(0)] AS Status,
LOGICAL_OR(Reason = 'CustomerTriggeredMaintenance') AS Is_CTM,

-- VALID CYCLE DEFINITION
(
ARRAY_AGG(Status ORDER BY status_rank ASC, EventTime DESC LIMIT 1)[OFFSET(0)] = 'COMPLETE' AND
MAX(EventTime) >= ARRAY_AGG(MaintenanceStartTime IGNORE NULLS ORDER BY EventTime DESC LIMIT 1)[OFFSET(0)]
) AS is_valid_cycle,

-- Get the EndTime of the most recent valid cycle
LAG(CASE
WHEN ARRAY_AGG(Status ORDER BY status_rank ASC, EventTime DESC LIMIT 1)[OFFSET(0)] = 'COMPLETE' AND
MAX(EventTime) >= ARRAY_AGG(MaintenanceStartTime IGNORE NULLS ORDER BY EventTime DESC LIMIT 1)[OFFSET(0)]
Expand All @@ -355,9 +353,10 @@ FROM (
FROM
MaintenanceData_Distinct
GROUP BY
CycleCount, Cluster, Nodepool, Node
CycleCount, Cluster, Node
)
WHERE Status NOT IN ('DELETED', 'UNKNOWN')

WHERE Status NOT IN ('DELETED', 'UNKNOWN') AND Type IS NOT NULL
ORDER BY MaintenanceEndTime
```

Expand Down
Loading
Loading