Skip to content

Commit 7c9e971

Browse files
committed
feat(runner/triggers): attach Critical insight to service_no_endpoints evidence
The investigate page renders per-block insights; the collector's only sources are Warning rows in event tables (a zero-endpoint Service emits no K8s events) and pod-status extraction from the raw-event json (the subject is a Service), so these findings surfaced with an empty Insights section. Ship the insight explicitly via additional_info .insights, which the collector passes through verbatim.
1 parent 77dad3f commit 7c9e971

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

runner/pkg/triggers/service_endpoints.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,31 @@ func serviceNoEndpointsEnrichBlocks(obj, _ map[string]any, ec EnrichContext) []E
107107
}
108108
sel := serviceSelector(obj)
109109
ns := metaNS(obj)
110+
condition := fmt.Sprintf(
111+
"No pods and no workload pod templates in namespace `%s` match this selector. "+
112+
"The Service has no endpoints and cannot route traffic — requests to it fail "+
113+
"even though the Service object itself looks healthy.", ns)
110114
blocks := []EvidenceBlock{
111115
headerBlock(fmt.Sprintf("Service %s has no matching endpoints", metaName(obj))),
112116
markdownBlock(fmt.Sprintf("*Configured selector:* `%s`", formatSelector(sel))),
113-
markdownBlock(fmt.Sprintf(
114-
"No pods and no workload pod templates in namespace `%s` match this selector. "+
115-
"The Service has no endpoints and cannot route traffic — requests to it fail "+
116-
"even though the Service object itself looks healthy.", ns)),
117+
// The collector passes additional_info.insights through to the
118+
// Finding's per-block insight list, which is what the investigate
119+
// page renders as Insights. Without this the page shows nothing:
120+
// its other insight sources are Warning rows in event tables (a
121+
// zero-endpoint Service emits no K8s events) and pod-status
122+
// extraction from the raw-event json (subject is a Service).
123+
{
124+
"type": "markdown",
125+
"data": condition,
126+
"additional_info": map[string]any{
127+
"insights": []map[string]any{{
128+
"message": fmt.Sprintf(
129+
"Service %s/%s selector (%s) matches no pods and no workload pod templates — traffic to the Service is failing",
130+
ns, metaName(obj), formatSelector(sel)),
131+
"severity": "Critical",
132+
}},
133+
},
134+
},
117135
}
118136
return append(blocks, podLabelComparisonTable(ec, ns)...)
119137
}

runner/pkg/triggers/service_endpoints_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,26 @@ func TestServiceNoEndpoints_EnrichBlocks(t *testing.T) {
149149
t.Error("evidence must include the pod-labels comparison table")
150150
}
151151

152+
// One block must carry additional_info.insights with severity Critical
153+
// — the collector passes it through to the Finding's insight list,
154+
// which is what the investigate page renders.
155+
haveInsight := false
156+
for _, b := range blocks {
157+
ai, _ := b["additional_info"].(map[string]any)
158+
if ai == nil {
159+
continue
160+
}
161+
ins, _ := ai["insights"].([]map[string]any)
162+
for _, i := range ins {
163+
if i["severity"] == "Critical" {
164+
haveInsight = true
165+
}
166+
}
167+
}
168+
if !haveInsight {
169+
t.Error("evidence must carry a Critical insight via additional_info.insights")
170+
}
171+
152172
// Empty namespace → the table degrades to a "no pods at all" note.
153173
ec = EnrichContext{ServiceBackends: &fakeServiceBackends{}}
154174
blocks = serviceNoEndpointsEnrichBlocks(brokenService(t), nil, ec)

0 commit comments

Comments
 (0)