From 208ca39f835b1173adf53143edb730564436f7c3 Mon Sep 17 00:00:00 2001 From: Yu Yi Date: Fri, 22 May 2026 16:21:27 -0400 Subject: [PATCH] Tighten pod resource attribution in the Stackdriver event sink When mapping a corev1.Event onto a Stackdriver MonitoredResource with InvolvedObject.Kind == "Pod", only emit a k8s_pod resource when the event's own metadata.namespace is set and equals event.InvolvedObject.Namespace. Use event.Namespace (the value the API server validates against RBAC at event creation time) to populate the namespace_name label. Events that fail the check fall back to the default cluster-scoped resource so the log content is still exported. Node attribution is intentionally unchanged in this PR; it will be addressed separately. Updates the existing pod test to populate event.Namespace and adds cases that cover the new fallback paths. --- .../stackdriver/log_entry_factory_test.go | 1 + .../stackdriver/monitored_resource_factory.go | 12 +++++- .../monitored_resource_factory_test.go | 38 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/event-exporter/sinks/stackdriver/log_entry_factory_test.go b/event-exporter/sinks/stackdriver/log_entry_factory_test.go index 46637852a..991ca57d5 100644 --- a/event-exporter/sinks/stackdriver/log_entry_factory_test.go +++ b/event-exporter/sinks/stackdriver/log_entry_factory_test.go @@ -111,6 +111,7 @@ func TestFromEvent(t *testing.T) { { desc: "k8s pod event with pod labels", event: &corev1.Event{ + ObjectMeta: metav1.ObjectMeta{Namespace: "test_namespace"}, Type: "Normal", InvolvedObject: involvedPodObject, LastTimestamp: lastTimestamp, diff --git a/event-exporter/sinks/stackdriver/monitored_resource_factory.go b/event-exporter/sinks/stackdriver/monitored_resource_factory.go index 51099572c..8da19ef4d 100644 --- a/event-exporter/sinks/stackdriver/monitored_resource_factory.go +++ b/event-exporter/sinks/stackdriver/monitored_resource_factory.go @@ -74,7 +74,15 @@ func (f *monitoredResourceFactory) resourceFromEvent(event *corev1.Event) *sd.Mo switch event.InvolvedObject.Kind { case pod: - monitoredResource = f.buildPodMonitoredResource(event) + // The event's own metadata.namespace is the RBAC-enforced source + // of truth for where the event was created. Only emit a pod-scoped + // resource when the involved object's namespace agrees with it; + // otherwise fall back to the default cluster-scoped resource. + if event.Namespace != "" && event.Namespace == event.InvolvedObject.Namespace { + monitoredResource = f.buildPodMonitoredResource(event) + } else { + monitoredResource = f.defaultResource + } case node: monitoredResource = f.buildNodeMonitoredResource(event) default: @@ -86,7 +94,7 @@ func (f *monitoredResourceFactory) resourceFromEvent(event *corev1.Event) *sd.Mo func (f *monitoredResourceFactory) buildPodMonitoredResource(event *corev1.Event) *sd.MonitoredResource { labels := copyMap(f.commonLabels) labels[podName] = event.InvolvedObject.Name - labels[namespaceName] = event.InvolvedObject.Namespace + labels[namespaceName] = event.Namespace return &sd.MonitoredResource{ Type: k8sPod, diff --git a/event-exporter/sinks/stackdriver/monitored_resource_factory_test.go b/event-exporter/sinks/stackdriver/monitored_resource_factory_test.go index edd7034c7..ccdd3891c 100644 --- a/event-exporter/sinks/stackdriver/monitored_resource_factory_test.go +++ b/event-exporter/sinks/stackdriver/monitored_resource_factory_test.go @@ -8,6 +8,7 @@ import ( "fmt" sd "google.golang.org/api/logging/v2" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestMonitoredResourceFromEvent(t *testing.T) { @@ -31,8 +32,11 @@ func TestMonitoredResourceFromEvent(t *testing.T) { }, }, { + // Pod event whose involvedObject namespace matches the event's + // own metadata namespace is attributed to the pod. config: newTypesConfig, event: &corev1.Event{ + ObjectMeta: metav1.ObjectMeta{Namespace: "test_pod_namespace"}, InvolvedObject: corev1.ObjectReference{Kind: pod, Name: "test_pod_name", Namespace: "test_pod_namespace"}, }, wanted: &sd.MonitoredResource{ @@ -46,6 +50,40 @@ func TestMonitoredResourceFromEvent(t *testing.T) { }, }, }, + { + // Pod event whose involvedObject namespace disagrees with the + // event's own metadata namespace must not be attributed to the + // claimed pod; fall back to the cluster resource. + config: newTypesConfig, + event: &corev1.Event{ + ObjectMeta: metav1.ObjectMeta{Namespace: "user_namespace"}, + InvolvedObject: corev1.ObjectReference{Kind: pod, Name: "test_pod_name", Namespace: "kube-system"}, + }, + wanted: &sd.MonitoredResource{ + Type: k8sCluster, + Labels: map[string]string{ + clusterName: newTypesConfig.clusterName, + location: newTypesConfig.location, + projectID: newTypesConfig.projectID, + }, + }, + }, + { + // Pod event with no event-level namespace cannot be attributed + // to a pod; fall back to the cluster resource. + config: newTypesConfig, + event: &corev1.Event{ + InvolvedObject: corev1.ObjectReference{Kind: pod, Name: "test_pod_name", Namespace: "kube-system"}, + }, + wanted: &sd.MonitoredResource{ + Type: k8sCluster, + Labels: map[string]string{ + clusterName: newTypesConfig.clusterName, + location: newTypesConfig.location, + projectID: newTypesConfig.projectID, + }, + }, + }, { config: newTypesConfig, event: &corev1.Event{