Skip to content

Commit f214dc8

Browse files
ronkorlandRon Korlandedmocostaevan-bradley
authored
[pkg/ottl] Add client metadata access to OTTL contexts (open-telemetry#41879)
Co-authored-by: Ron Korland <ron@sawmills.ai> Co-authored-by: Edmo Vamerlatti Costa <11836452+edmocosta@users.noreply.github.com> Co-authored-by: Evan Bradley <11745660+evan-bradley@users.noreply.github.com>
1 parent 7154bf1 commit f214dc8

52 files changed

Lines changed: 1601 additions & 132 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.chloggen/33288-metadata.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Added metadata access path to all OTTL contexts for accessing client request metadata
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [33288]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

connector/countconnector/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ require (
8282
github.com/yusufpapurcu/wmi v1.2.4 // indirect
8383
github.com/zeebo/xxh3 v1.1.0 // indirect
8484
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
85+
go.opentelemetry.io/collector/client v1.52.1-0.20260227062254-168030d61d7d // indirect
8586
go.opentelemetry.io/collector/component/componentstatus v0.146.2-0.20260227062254-168030d61d7d // indirect
8687
go.opentelemetry.io/collector/config/configtelemetry v0.146.2-0.20260227062254-168030d61d7d // indirect
8788
go.opentelemetry.io/collector/confmap/provider/envprovider v1.52.1-0.20260227062254-168030d61d7d // indirect

connector/signaltometricsconnector/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ require (
6363
github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6 // indirect
6464
github.com/zeebo/xxh3 v1.1.0 // indirect
6565
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
66+
go.opentelemetry.io/collector/client v1.52.1-0.20260227062254-168030d61d7d // indirect
6667
go.opentelemetry.io/collector/consumer/xconsumer v0.146.2-0.20260227062254-168030d61d7d // indirect
6768
go.opentelemetry.io/collector/featuregate v1.52.1-0.20260227062254-168030d61d7d // indirect
6869
go.opentelemetry.io/collector/internal/componentalias v0.146.2-0.20260227062254-168030d61d7d // indirect
@@ -79,6 +80,7 @@ require (
7980
golang.org/x/net v0.51.0 // indirect
8081
golang.org/x/sys v0.41.0 // indirect
8182
golang.org/x/text v0.34.0 // indirect
83+
google.golang.org/grpc v1.79.1 // indirect
8284
gopkg.in/yaml.v2 v2.4.0 // indirect
8385
gopkg.in/yaml.v3 v3.0.1 // indirect
8486
)

connector/signaltometricsconnector/go.sum

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

connector/sumconnector/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ require (
5858
github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6 // indirect
5959
github.com/zeebo/xxh3 v1.1.0 // indirect
6060
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
61+
go.opentelemetry.io/collector/client v1.52.1-0.20260227062254-168030d61d7d // indirect
6162
go.opentelemetry.io/collector/connector/xconnector v0.146.2-0.20260227062254-168030d61d7d // indirect
6263
go.opentelemetry.io/collector/consumer/xconsumer v0.146.2-0.20260227062254-168030d61d7d // indirect
6364
go.opentelemetry.io/collector/featuregate v1.52.1-0.20260227062254-168030d61d7d // indirect
@@ -77,6 +78,7 @@ require (
7778
golang.org/x/net v0.51.0 // indirect
7879
golang.org/x/sys v0.41.0 // indirect
7980
golang.org/x/text v0.34.0 // indirect
81+
google.golang.org/grpc v1.79.1 // indirect
8082
gopkg.in/yaml.v2 v2.4.0 // indirect
8183
gopkg.in/yaml.v3 v3.0.1 // indirect
8284
)

connector/sumconnector/go.sum

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/filter/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ require (
5252
github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6 // indirect
5353
github.com/zeebo/xxh3 v1.1.0 // indirect
5454
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
55+
go.opentelemetry.io/collector/client v1.52.1-0.20260227062254-168030d61d7d // indirect
5556
go.opentelemetry.io/collector/pdata/pprofile v0.146.2-0.20260227062254-168030d61d7d // indirect
5657
go.opentelemetry.io/otel/metric v1.40.0 // indirect
5758
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
@@ -63,6 +64,7 @@ require (
6364
golang.org/x/net v0.51.0 // indirect
6465
golang.org/x/sys v0.41.0 // indirect
6566
golang.org/x/text v0.34.0 // indirect
67+
google.golang.org/grpc v1.79.1 // indirect
6668
gopkg.in/yaml.v2 v2.4.0 // indirect
6769
gopkg.in/yaml.v3 v3.0.1 // indirect
6870
)

internal/filter/go.sum

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package ctxotelcol // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal/ctxotelcol"
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
11+
"go.opentelemetry.io/collector/client"
12+
"go.opentelemetry.io/collector/pdata/pcommon"
13+
14+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
15+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal/ctxerror"
16+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal/ctxutil"
17+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/internal/ottlcommon"
18+
)
19+
20+
func accessClient[K any](path ottl.Path[K]) (ottl.GetSetter[K], error) {
21+
nextPath := path.Next()
22+
if nextPath == nil {
23+
return nil, ctxerror.New(path.Name(), path.String(), Name, DocRef)
24+
}
25+
switch nextPath.Name() {
26+
case "addr":
27+
return accessClientAddr(nextPath)
28+
case "auth":
29+
return accessClientAuth(nextPath)
30+
case "metadata":
31+
return accessClientMetadata(nextPath)
32+
default:
33+
return nil, ctxerror.New(nextPath.Name(), nextPath.String(), Name, DocRef)
34+
}
35+
}
36+
37+
func accessClientMetadata[K any](path ottl.Path[K]) (ottl.GetSetter[K], error) {
38+
nextPath := path.Next()
39+
if nextPath != nil {
40+
return nil, ctxerror.New(nextPath.Name(), nextPath.String(), Name, DocRef)
41+
}
42+
if path.Keys() == nil {
43+
return accessClientMetadataKeys[K](), nil
44+
}
45+
return accessClientMetadataKey[K](path.Keys()), nil
46+
}
47+
48+
func accessClientAddr[K any](path ottl.Path[K]) (ottl.GetSetter[K], error) {
49+
nextPath := path.Next()
50+
if nextPath != nil {
51+
return nil, ctxerror.New(nextPath.Name(), nextPath.String(), Name, DocRef)
52+
}
53+
if path.Keys() != nil {
54+
return nil, ctxerror.New(path.Name(), path.String(), Name, DocRef)
55+
}
56+
return ottl.StandardGetSetter[K]{
57+
Getter: func(ctx context.Context, _ K) (any, error) {
58+
cl := client.FromContext(ctx)
59+
if cl.Addr == nil {
60+
return nil, nil
61+
}
62+
return cl.Addr.String(), nil
63+
},
64+
Setter: func(_ context.Context, _ K, _ any) error {
65+
return fmt.Errorf(readOnlyPathErrMsg, "otelcol.client.addr")
66+
},
67+
}, nil
68+
}
69+
70+
func getAuthAttributeValue(authData client.AuthData, key string) (pcommon.Value, error) {
71+
attrVal := authData.GetAttribute(key)
72+
switch typedAttrVal := attrVal.(type) {
73+
case string:
74+
return pcommon.NewValueStr(typedAttrVal), nil
75+
case []string:
76+
value := pcommon.NewValueSlice()
77+
slice := value.Slice()
78+
slice.EnsureCapacity(len(typedAttrVal))
79+
for _, str := range typedAttrVal {
80+
slice.AppendEmpty().SetStr(str)
81+
}
82+
return value, nil
83+
default:
84+
value := pcommon.NewValueEmpty()
85+
err := value.FromRaw(attrVal)
86+
if err != nil {
87+
return pcommon.Value{}, err
88+
}
89+
return value, nil
90+
}
91+
}
92+
93+
func convertAuthDataToMap(authData client.AuthData) pcommon.Map {
94+
authMap := pcommon.NewMap()
95+
if authData == nil {
96+
return authMap
97+
}
98+
names := authData.GetAttributeNames()
99+
authMap.EnsureCapacity(len(names))
100+
for _, name := range names {
101+
newKeyValue := authMap.PutEmpty(name)
102+
if value, err := getAuthAttributeValue(authData, name); err == nil {
103+
value.MoveTo(newKeyValue)
104+
}
105+
}
106+
return authMap
107+
}
108+
109+
func accessClientAuth[K any](path ottl.Path[K]) (ottl.GetSetter[K], error) {
110+
nextPath := path.Next()
111+
if nextPath == nil {
112+
return nil, ctxerror.New(path.Name(), path.String(), Name, DocRef)
113+
}
114+
switch nextPath.Name() {
115+
case "attributes":
116+
if nextPath.Keys() == nil {
117+
return accessClientAuthAttributesKeys[K](), nil
118+
}
119+
return accessClientAuthAttributesKey[K](nextPath.Keys()), nil
120+
default:
121+
return nil, ctxerror.New(nextPath.Name(), nextPath.String(), Name, DocRef)
122+
}
123+
}
124+
125+
func accessClientAuthAttributesKeys[K any]() ottl.StandardGetSetter[K] {
126+
return ottl.StandardGetSetter[K]{
127+
Getter: func(ctx context.Context, _ K) (any, error) {
128+
cl := client.FromContext(ctx)
129+
return convertAuthDataToMap(cl.Auth), nil
130+
},
131+
Setter: func(_ context.Context, _ K, _ any) error {
132+
return fmt.Errorf(readOnlyPathErrMsg, "otelcol.client.auth.attributes")
133+
},
134+
}
135+
}
136+
137+
func accessClientAuthAttributesKey[K any](keys []ottl.Key[K]) ottl.StandardGetSetter[K] {
138+
return ottl.StandardGetSetter[K]{
139+
Getter: func(ctx context.Context, tCtx K) (any, error) {
140+
if len(keys) == 0 {
141+
return nil, errors.New("cannot get map value without keys")
142+
}
143+
cl := client.FromContext(ctx)
144+
key, err := ctxutil.GetMapKeyName(ctx, tCtx, keys[0])
145+
if err != nil {
146+
return nil, err
147+
}
148+
if cl.Auth == nil {
149+
return nil, nil
150+
}
151+
attrVal, err := getAuthAttributeValue(cl.Auth, *key)
152+
if err != nil {
153+
return nil, err
154+
}
155+
if len(keys) > 1 {
156+
switch attrVal.Type() {
157+
case pcommon.ValueTypeSlice:
158+
return ctxutil.GetSliceValue[K](ctx, tCtx, attrVal.Slice(), keys[1:])
159+
case pcommon.ValueTypeMap:
160+
return ctxutil.GetMapValue[K](ctx, tCtx, attrVal.Map(), keys[1:])
161+
default:
162+
return nil, fmt.Errorf("attribute %q value is not indexable: %s", *key, attrVal.Type().String())
163+
}
164+
}
165+
return ottlcommon.GetValue(attrVal), nil
166+
},
167+
Setter: func(_ context.Context, _ K, _ any) error {
168+
return fmt.Errorf(readOnlyPathErrMsg, "otelcol.client.auth.attributes")
169+
},
170+
}
171+
}
172+
173+
func convertClientMetadataToMap(md client.Metadata) pcommon.Map {
174+
mdMap := pcommon.NewMap()
175+
for k := range md.Keys() {
176+
convertStringArrToValueSlice(md.Get(k)).MoveTo(mdMap.PutEmpty(k))
177+
}
178+
return mdMap
179+
}
180+
181+
func accessClientMetadataKeys[K any]() ottl.StandardGetSetter[K] {
182+
return ottl.StandardGetSetter[K]{
183+
Getter: func(ctx context.Context, _ K) (any, error) {
184+
cl := client.FromContext(ctx)
185+
return convertClientMetadataToMap(cl.Metadata), nil
186+
},
187+
Setter: func(_ context.Context, _ K, _ any) error {
188+
return fmt.Errorf(readOnlyPathErrMsg, "otelcol.client.metadata")
189+
},
190+
}
191+
}
192+
193+
func accessClientMetadataKey[K any](keys []ottl.Key[K]) ottl.StandardGetSetter[K] {
194+
return ottl.StandardGetSetter[K]{
195+
Getter: func(ctx context.Context, tCtx K) (any, error) {
196+
if len(keys) == 0 {
197+
return nil, errors.New("cannot get map value without keys")
198+
}
199+
200+
key, err := ctxutil.GetMapKeyName(ctx, tCtx, keys[0])
201+
if err != nil {
202+
return nil, fmt.Errorf("cannot get map value: %w", err)
203+
}
204+
cl := client.FromContext(ctx)
205+
mdVal := cl.Metadata.Get(*key)
206+
if len(mdVal) == 0 {
207+
return nil, nil
208+
}
209+
return getIndexableValueFromStringArr(ctx, tCtx, keys[1:], mdVal)
210+
},
211+
Setter: func(_ context.Context, _ K, _ any) error {
212+
return fmt.Errorf(readOnlyPathErrMsg, "otelcol.client.metadata")
213+
},
214+
}
215+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package ctxotelcol // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal/ctxotelcol"
5+
6+
const (
7+
readOnlyPathErrMsg = "%q is read-only and cannot be modified"
8+
Name = "otelcol"
9+
DocRef = "https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottlotelcol"
10+
)

0 commit comments

Comments
 (0)