@@ -3,6 +3,7 @@ package checks
33import (
44 "context"
55 "fmt"
6+ "slices"
67 "time"
78
89 "github.com/DataDog/datadog-agent/pkg/network"
@@ -19,7 +20,12 @@ import (
1920 "go.opentelemetry.io/otel/metric"
2021)
2122
23+ // Attribute keys for pod correlation metrics
2224const (
25+ // Please bump this every time you add a new attribute key
26+ // this is used by tests to ensure we don't forget to update the list of all keys
27+ numAttributeKeys = 11
28+
2329 // We export them so that we can reuse these fields in e2e tests
2430
2531 // DirectionKey is the direction of the connection (0=outgoing, 1=incoming)
@@ -52,6 +58,32 @@ const (
5258 RemoteLabelsKey = "remote.pod.label"
5359)
5460
61+ var (
62+ // Keep this updated with all the possible keys
63+ AllAttributeKeys = []string {
64+ DirectionKey ,
65+ LocalIPKey ,
66+ LocalPortKey ,
67+ LocalPodNameKey ,
68+ LocalNSKey ,
69+ LocalLabelsKey ,
70+ RemoteIPKey ,
71+ RemotePortKey ,
72+ RemotePodNameKey ,
73+ RemoteNSKey ,
74+ RemoteLabelsKey ,
75+ }
76+
77+ DefaultAttributeKeys = []string {
78+ DirectionKey ,
79+ LocalPodNameKey ,
80+ LocalNSKey ,
81+ LocalLabelsKey ,
82+ RemotePodNameKey ,
83+ RemoteNSKey ,
84+ }
85+ )
86+
5587type storedConnection struct {
5688 conn * network.ConnectionStats
5789 protocolMetrics []* model.ConnectionMetric
@@ -65,26 +97,61 @@ type podCorrelationInfo struct {
6597 exportPartialCorrelation bool
6698 storedConnections []* storedConnection
6799 rootNSIno uint32
100+ attributesKeys []string
101+ }
102+
103+ func validateAttributeKeys (keys []string ) ([]string , error ) {
104+ if len (keys ) == 0 {
105+ // return the default keys
106+ return DefaultAttributeKeys , nil
107+ }
108+
109+ // We want unique keys
110+ slices .Sort (keys )
111+ keys = slices .Compact (keys )
112+
113+ for _ , key := range keys {
114+ if ! slices .Contains (AllAttributeKeys , key ) {
115+ return nil , fmt .Errorf ("invalid attribute key: '%s'" , key )
116+ }
117+ }
118+ return keys , nil
68119}
69120
70121func newPodCorrelationInfo (cfg * config.PodCorrelationConfig , logLevel string , rootNSIno uint32 ) (* podCorrelationInfo , error ) {
122+ log .Infof ("Pod correlation enabled (protocol_metrics: %v), (partial_correlation: %v)" , cfg .ProtocolMetrics , cfg .PartialCorrelation )
123+
124+ attrs , err := validateAttributeKeys (cfg .AttributesKeys )
125+ if err != nil {
126+ return nil , err
127+ }
128+
129+ log .Info ("Using the following attribute keys for pod correlated metrics: " , attrs )
130+
71131 podCorrelationInfo := & podCorrelationInfo {
72132 exportProtocolMetrics : cfg .ProtocolMetrics ,
73133 exportPartialCorrelation : cfg .PartialCorrelation ,
74134 storedConnections : make ([]* storedConnection , 0 ),
75135 rootNSIno : rootNSIno ,
136+ attributesKeys : attrs ,
76137 }
77138
78- // if we are in tests we don't want to start the informer
79- if cfg .Exporter .Type != config .ExporterTypeManual {
139+ switch cfg .Exporter .Type {
140+ case config .ExporterTypeDisabled :
141+ // Used only in tests to disable exporting and avoid starting the informer
142+ case config .ExporterTypeManual :
143+ // Here we only want the exporter
144+ if podCorrelationInfo .metrics , err = telemetry .NewMetricsExporter (cfg .Exporter ); err != nil {
145+ return nil , err
146+ }
147+ case config .ExporterTypeStdout , config .ExporterTypeOTLP :
148+ // Here we want the informer and the exporter
80149 if err := podCorrelationInfo .startKubernetesInformer (cfg , logLevel ); err != nil {
81150 return nil , err
82151 }
83- }
84-
85- var err error
86- if podCorrelationInfo .metrics , err = telemetry .NewMetricsExporter (cfg .Exporter ); err != nil {
87- return nil , err
152+ if podCorrelationInfo .metrics , err = telemetry .NewMetricsExporter (cfg .Exporter ); err != nil {
153+ return nil , err
154+ }
88155 }
89156
90157 return podCorrelationInfo , nil
@@ -115,7 +182,7 @@ func (pi *podCorrelationInfo) startKubernetesInformer(cfg *config.PodCorrelation
115182}
116183
117184func (pi * podCorrelationInfo ) generateConnectionMetrics (conn * network.ConnectionStats , srcPodInfo , dstPodInfo * kube.PodInfo ) {
118- attrs := attribute .NewSet (getMetricAttributes (conn , srcPodInfo , dstPodInfo )... )
185+ attrs := attribute .NewSet (pi . getMetricAttributes (conn , srcPodInfo , dstPodInfo )... )
119186
120187 pi .metrics .BytesRecv .Add (context .Background (), int64 (conn .Last .RecvBytes ), metric .WithAttributeSet (attrs ))
121188 pi .metrics .BytesSent .Add (context .Background (), int64 (conn .Last .SentBytes ), metric .WithAttributeSet (attrs ))
@@ -136,33 +203,53 @@ func addPrefixToLabels(prefix string, labels map[string]string) []attribute.KeyV
136203 return otelLabels
137204}
138205
139- func getMetricAttributes (conn * network.ConnectionStats , localPodInfo , remotePodInfo * kube.PodInfo ) []attribute.KeyValue {
140- attributes := []attribute.KeyValue {
141- attribute .String (LocalIPKey , conn .ConnectionTuple .Source .String ()),
142- attribute .String (LocalPortKey , fmt .Sprintf ("%d" , conn .ConnectionTuple .SPort )),
143- attribute .String (RemoteIPKey , conn .ConnectionTuple .Dest .String ()),
144- attribute .String (RemotePortKey , fmt .Sprintf ("%d" , conn .ConnectionTuple .DPort )),
145- attribute .String (DirectionKey , connectionDirectionToString (conn .Direction )),
146- }
147- if localPodInfo != nil {
148- attributes = append (attributes ,
149- attribute .String (LocalPodNameKey , localPodInfo .Name ),
150- attribute .String (LocalNSKey , localPodInfo .Namespace ),
151- )
152- attributes = append (attributes , addPrefixToLabels (LocalLabelsKey , localPodInfo .Labels )... )
153- }
154- if remotePodInfo != nil {
155- attributes = append (attributes ,
156- attribute .String (RemotePodNameKey , remotePodInfo .Name ),
157- attribute .String (RemoteNSKey , remotePodInfo .Namespace ),
158- )
159- attributes = append (attributes , addPrefixToLabels (RemoteLabelsKey , remotePodInfo .Labels )... )
206+ func (pi * podCorrelationInfo ) getMetricAttributes (conn * network.ConnectionStats , localPodInfo , remotePodInfo * kube.PodInfo ) []attribute.KeyValue {
207+ attributes := make ([]attribute.KeyValue , 0 , len (pi .attributesKeys ))
208+ for _ , key := range pi .attributesKeys {
209+ switch key {
210+ case LocalIPKey :
211+ attributes = append (attributes , attribute .String (LocalIPKey , conn .ConnectionTuple .Source .String ()))
212+ case LocalPortKey :
213+ attributes = append (attributes , attribute .String (LocalPortKey , fmt .Sprintf ("%d" , conn .ConnectionTuple .SPort )))
214+ case RemoteIPKey :
215+ attributes = append (attributes , attribute .String (RemoteIPKey , conn .ConnectionTuple .Dest .String ()))
216+ case RemotePortKey :
217+ attributes = append (attributes , attribute .String (RemotePortKey , fmt .Sprintf ("%d" , conn .ConnectionTuple .DPort )))
218+ case DirectionKey :
219+ attributes = append (attributes , attribute .String (DirectionKey , connectionDirectionToString (conn .Direction )))
220+ case LocalPodNameKey :
221+ if localPodInfo != nil {
222+ attributes = append (attributes , attribute .String (LocalPodNameKey , localPodInfo .Name ))
223+ }
224+ case LocalNSKey :
225+ if localPodInfo != nil {
226+ attributes = append (attributes , attribute .String (LocalNSKey , localPodInfo .Namespace ))
227+ }
228+ case LocalLabelsKey :
229+ if localPodInfo != nil {
230+ attributes = append (attributes , addPrefixToLabels (LocalLabelsKey , localPodInfo .Labels )... )
231+ }
232+ case RemotePodNameKey :
233+ if remotePodInfo != nil {
234+ attributes = append (attributes , attribute .String (RemotePodNameKey , remotePodInfo .Name ))
235+ }
236+ case RemoteNSKey :
237+ if remotePodInfo != nil {
238+ attributes = append (attributes , attribute .String (RemoteNSKey , remotePodInfo .Namespace ))
239+ }
240+ case RemoteLabelsKey :
241+ if remotePodInfo != nil {
242+ attributes = append (attributes , addPrefixToLabels (RemoteLabelsKey , remotePodInfo .Labels )... )
243+ }
244+ default :
245+ panic (fmt .Sprintf ("Unknown attribute key: %s" , key ))
246+ }
160247 }
161248 return attributes
162249}
163250
164251func (pi * podCorrelationInfo ) generateProtocolMetrics (conn * network.ConnectionStats , srcPodInfo , dstPodInfo * kube.PodInfo , metrics []* model.ConnectionMetric ) {
165- attr := getMetricAttributes (conn , srcPodInfo , dstPodInfo )
252+ attr := pi . getMetricAttributes (conn , srcPodInfo , dstPodInfo )
166253
167254 for _ , m := range metrics {
168255 // todo!: for now we only support postgres metrics.
0 commit comments