@@ -27,18 +27,41 @@ import (
2727
2828 "github.com/stretchr/testify/assert"
2929 "github.com/stretchr/testify/require"
30+ "go.opentelemetry.io/collector/pdata/pcommon"
3031 "go.uber.org/goleak"
3132 "k8s.io/apimachinery/pkg/runtime"
3233 k8sclient "k8s.io/client-go/kubernetes"
3334 k8sfake "k8s.io/client-go/kubernetes/fake"
3435 k8stesting "k8s.io/client-go/testing"
3536
3637 "github.com/elastic/beats/v7/libbeat/beat"
38+ "github.com/elastic/beats/v7/libbeat/otel/otelmap"
39+ "github.com/elastic/beats/v7/libbeat/processors"
3740 "github.com/elastic/elastic-agent-libs/config"
3841 "github.com/elastic/elastic-agent-libs/logp/logptest"
3942 "github.com/elastic/elastic-agent-libs/mapstr"
4043)
4144
45+ // assertRunPdataEquivalent verifies that RunPdata, given the same input fields
46+ // used to produce result via Run, enriches a pcommon.Map with identical output.
47+ func assertRunPdataEquivalent (t * testing.T , p beat.Processor , input , result mapstr.M ) {
48+ t .Helper ()
49+
50+ pp , ok := p .(processors.PdataProcessor )
51+ require .True (t , ok , "processor must implement PdataProcessor" )
52+
53+ body := pcommon .NewMap ()
54+ require .NoError (t , otelmap .FromMapstr (body , input ))
55+ drop , err := pp .RunPdata (body )
56+ require .NoError (t , err )
57+ require .False (t , drop )
58+
59+ legacyNorm := pcommon .NewMap ()
60+ require .NoError (t , otelmap .FromMapstr (legacyNorm , result ))
61+ assert .Equal (t , otelmap .ToMapstr (legacyNorm ), otelmap .ToMapstr (body ),
62+ "Run and RunPdata must produce identical output" )
63+ }
64+
4265// Test Annotator is skipped if kubernetes metadata already exist
4366func TestAnnotatorSkipped (t * testing.T ) {
4467 cfg := config .MustNewConfigFrom (map [string ]interface {}{
@@ -67,23 +90,7 @@ func TestAnnotatorSkipped(t *testing.T) {
6790 },
6891 })
6992
70- event , err := processor .Run (& beat.Event {
71- Fields : mapstr.M {
72- "kubernetes" : mapstr.M {
73- "pod" : mapstr.M {
74- "name" : "foo" ,
75- "id" : "pod_id" ,
76- "metrics" : mapstr.M {
77- "a" : 1 ,
78- "b" : 2 ,
79- },
80- },
81- },
82- },
83- })
84- assert .NoError (t , err )
85-
86- assert .Equal (t , mapstr.M {
93+ input := mapstr.M {
8794 "kubernetes" : mapstr.M {
8895 "pod" : mapstr.M {
8996 "name" : "foo" ,
@@ -94,7 +101,14 @@ func TestAnnotatorSkipped(t *testing.T) {
94101 },
95102 },
96103 },
97- }, event .Fields )
104+ }
105+ event , err := processor .Run (& beat.Event {Fields : input .Clone ()})
106+ assert .NoError (t , err )
107+
108+ assert .Equal (t , input , event .Fields )
109+
110+ // RunPdata path: assert Run == RunPdata.
111+ assertRunPdataEquivalent (t , & processor , input , event .Fields )
98112}
99113
100114// Test metadata are not included in the event
@@ -123,6 +137,9 @@ func TestAnnotatorWithNoKubernetesAvailable(t *testing.T) {
123137 assert .NoError (t , err )
124138
125139 assert .Equal (t , intialEventMap , event .Fields )
140+
141+ // RunPdata path: assert Run == RunPdata.
142+ assertRunPdataEquivalent (t , & processor , intialEventMap , event .Fields )
126143}
127144
128145// TestNewProcessorConfigDefaultIndexers validates the behaviour of default indexers and
@@ -317,6 +334,9 @@ func TestAnnotatorRunFullContainerMetadata(t *testing.T) {
317334 assert .NotContains (t , k8sContainer , "id" , "kubernetes.container must NOT have id" )
318335 assert .NotContains (t , k8sContainer , "runtime" , "kubernetes.container must NOT have runtime" )
319336 assert .NotContains (t , k8sContainer , "image" , "kubernetes.container must NOT have image" )
337+
338+ // RunPdata path: assert Run == RunPdata.
339+ assertRunPdataEquivalent (t , processor , mapstr.M {"container" : mapstr.M {"id" : "abc123" }}, event .Fields )
320340}
321341
322342// TestAnnotatorRunContainerWithoutImage verifies that when there is no image in
@@ -345,6 +365,9 @@ func TestAnnotatorRunContainerWithoutImage(t *testing.T) {
345365 assert .Equal (t , "abc456" , container ["id" ])
346366 assert .Equal (t , "docker" , container ["runtime" ])
347367 assert .NotContains (t , container , "image" , "container must NOT have image key when no image in metadata" )
368+
369+ // RunPdata path: assert Run == RunPdata.
370+ assertRunPdataEquivalent (t , processor , baseEvent ("abc456" ).Fields , event .Fields )
348371}
349372
350373// TestAnnotatorRunContainerWithoutName verifies that missing container.name
@@ -375,6 +398,9 @@ func TestAnnotatorRunContainerWithoutName(t *testing.T) {
375398 require .IsType (t , mapstr.M {}, imageRaw )
376399 imageMap , _ := imageRaw .(mapstr.M )
377400 assert .Equal (t , "busybox:latest" , imageMap ["name" ])
401+
402+ // RunPdata path: assert Run == RunPdata.
403+ assertRunPdataEquivalent (t , processor , baseEvent ("abc789" ).Fields , event .Fields )
378404}
379405
380406// TestAnnotatorRunNoContainerSubMap verifies that when the metadata has no
@@ -426,6 +452,9 @@ func TestAnnotatorRunNoContainerSubMap(t *testing.T) {
426452 require .IsType (t , mapstr.M {}, podRaw )
427453 pod , _ := podRaw .(mapstr.M )
428454 assert .Equal (t , "mypod" , pod ["name" ])
455+
456+ // RunPdata path: assert Run == RunPdata.
457+ assertRunPdataEquivalent (t , processor , mapstr.M {"pod" : mapstr.M {"name" : "mypod" }}, event .Fields )
429458}
430459
431460// TestAnnotatorRunExtraContainerFieldsPreserved verifies that unknown extra
@@ -454,6 +483,9 @@ func TestAnnotatorRunExtraContainerFieldsPreserved(t *testing.T) {
454483 container , _ := containerRaw .(mapstr.M )
455484
456485 assert .Equal (t , "extra" , container ["custom_field" ], "extra container fields must be preserved in OCI container" )
486+
487+ // RunPdata path: assert Run == RunPdata.
488+ assertRunPdataEquivalent (t , processor , baseEvent ("xtra001" ).Fields , event .Fields )
457489}
458490
459491// TestAnnotatorRunCacheNotMutated verifies that running the processor multiple
@@ -536,6 +568,24 @@ func TestAnnotatorRunEventIndependence(t *testing.T) {
536568 assert .NotContains (t , container2 , "injected" , "mutating first result must not affect second result" )
537569}
538570
571+ // TestAnnotatorRunCacheMiss verifies that when the matcher returns an index key
572+ // but the cache has no entry for it, both Run and RunPdata leave the event
573+ // unchanged and return no error.
574+ func TestAnnotatorRunCacheMiss (t * testing.T ) {
575+ // Seed the cache with a different key so the matcher can fire but miss.
576+ processor := newAnnotatorForTest (t , "known-key" , mapstr.M {
577+ "kubernetes" : mapstr.M {"pod" : mapstr.M {"name" : "mypod" }},
578+ })
579+
580+ input := mapstr.M {"container" : mapstr.M {"id" : "unknown-key" }}
581+ event , err := processor .Run (& beat.Event {Fields : input .Clone ()})
582+ require .NoError (t , err )
583+ assert .Equal (t , input , event .Fields , "Run must leave the event unchanged on a cache miss" )
584+
585+ // RunPdata path: assert Run == RunPdata.
586+ assertRunPdataEquivalent (t , processor , input , event .Fields )
587+ }
588+
539589func BenchmarkKubernetesAnnotatorRun (b * testing.B ) {
540590 cfg := config .MustNewConfigFrom (map [string ]interface {}{
541591 "lookup_fields" : []string {"container.id" },
0 commit comments