Skip to content

Commit ec9bbb2

Browse files
committed
Allow non-identifying attrs to be specified
1 parent 885ccf7 commit ec9bbb2

17 files changed

+261
-7
lines changed

.chloggen/allow-non-identifying.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
5+
component: opamp
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Allows specifying non-identifying attributes for the Bridge, also follows OpAMP guidelines for reporting instance.service.id
9+
10+
# One or more tracking issues related to the change
11+
issues: [2301]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext:

apis/v1alpha1/opampbridge_types.go

+9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ type OpAMPBridgeSpec struct {
2323
// ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.)
2424
// +optional
2525
ComponentsAllowed map[string][]string `json:"componentsAllowed,omitempty"`
26+
// Description allows the customization of the non identifying attributes for the OpAMP Bridge.
27+
// +optional
28+
Description *AgentDescription `json:"description,omitempty"`
2629
// Resources to set on the OpAMPBridge pods.
2730
// +optional
2831
Resources v1.ResourceRequirements `json:"resources,omitempty"`
@@ -111,6 +114,12 @@ type OpAMPBridgeStatus struct {
111114
Version string `json:"version,omitempty"`
112115
}
113116

117+
type AgentDescription struct {
118+
// NonIdentifyingAttributes are a map of key-value pairs that may be specified to provide
119+
// extra information about the agent to the OpAMP server.
120+
NonIdentifyingAttributes map[string]string `json:"non_identifying_attributes"`
121+
}
122+
114123
// +kubebuilder:object:root=true
115124
// +kubebuilder:subresource:status
116125
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

apis/v1alpha1/zz_generated.deepcopy.go

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ metadata:
9999
categories: Logging & Tracing,Monitoring
100100
certified: "false"
101101
containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
102-
createdAt: "2025-03-05T15:50:41Z"
102+
createdAt: "2025-03-12T19:10:16Z"
103103
description: Provides the OpenTelemetry components, including the Collector
104104
operators.operatorframework.io/builder: operator-sdk-v1.29.0
105105
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3

bundle/community/manifests/opentelemetry.io_opampbridges.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,15 @@ spec:
489489
type: string
490490
type: array
491491
type: object
492+
description:
493+
properties:
494+
non_identifying_attributes:
495+
additionalProperties:
496+
type: string
497+
type: object
498+
required:
499+
- non_identifying_attributes
500+
type: object
492501
endpoint:
493502
type: string
494503
env:

bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ metadata:
9999
categories: Logging & Tracing,Monitoring
100100
certified: "false"
101101
containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
102-
createdAt: "2025-03-05T15:50:41Z"
102+
createdAt: "2025-03-12T19:10:16Z"
103103
description: Provides the OpenTelemetry components, including the Collector
104104
operators.operatorframework.io/builder: operator-sdk-v1.29.0
105105
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3

bundle/openshift/manifests/opentelemetry.io_opampbridges.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,15 @@ spec:
489489
type: string
490490
type: array
491491
type: object
492+
description:
493+
properties:
494+
non_identifying_attributes:
495+
additionalProperties:
496+
type: string
497+
type: object
498+
required:
499+
- non_identifying_attributes
500+
type: object
492501
endpoint:
493502
type: string
494503
env:

cmd/operator-opamp-bridge/agent/agent.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func NewAgent(logger logr.Logger, applier operator.ConfigApplier, config *config
5858
applier: applier,
5959
logger: logger,
6060
appliedKeys: map[kubeResourceKey]bool{},
61-
instanceId: config.GetNewInstanceId(),
61+
instanceId: config.GetInstanceId(),
6262
agentDescription: config.GetDescription(),
6363
remoteConfigEnabled: config.RemoteConfigEnabled(),
6464
opampClient: opampClient,

cmd/operator-opamp-bridge/config/config.go

+35-3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type Config struct {
8181
ListenAddr string `yaml:"listenAddr,omitempty"`
8282
ClusterConfig *rest.Config `yaml:"-"`
8383
RootLogger logr.Logger `yaml:"-"`
84+
instanceId uuid.UUID `yaml:"-"`
8485

8586
// ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.)
8687
ComponentsAllowed map[string][]string `yaml:"componentsAllowed,omitempty"`
@@ -89,10 +90,20 @@ type Config struct {
8990
Capabilities map[Capability]bool `yaml:"capabilities"`
9091
HeartbeatInterval time.Duration `yaml:"heartbeatInterval,omitempty"`
9192
Name string `yaml:"name,omitempty"`
93+
AgentDescription AgentDescription `yaml:"description,omitempty"`
94+
}
95+
96+
// AgentDescription is copied from the OpAMP Extension in the collector.
97+
// https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/ccc3e6ed6386d404eb4beddd257ff979d2a346f4/extension/opampextension/config.go#L48
98+
type AgentDescription struct {
99+
// NonIdentifyingAttributes are a map of key-value pairs that may be specified to provide
100+
// extra information about the agent to the OpAMP server.
101+
NonIdentifyingAttributes map[string]string `yaml:"non_identifying_attributes"`
92102
}
93103

94104
func NewConfig(logger logr.Logger) *Config {
95105
return &Config{
106+
instanceId: mustGetInstanceId(),
96107
RootLogger: logger,
97108
}
98109
}
@@ -150,17 +161,33 @@ func (c *Config) GetAgentVersion() string {
150161
return agentVersion
151162
}
152163

164+
func (c *Config) GetInstanceId() uuid.UUID {
165+
return c.instanceId
166+
}
167+
153168
func (c *Config) GetDescription() *protobufs.AgentDescription {
154169
return &protobufs.AgentDescription{
155170
IdentifyingAttributes: []*protobufs.KeyValue{
156171
keyValuePair("service.name", c.GetAgentType()),
172+
keyValuePair("service.instance.id", c.GetInstanceId().String()),
157173
keyValuePair("service.version", c.GetAgentVersion()),
158174
},
159-
NonIdentifyingAttributes: []*protobufs.KeyValue{
175+
NonIdentifyingAttributes: append(
176+
c.AgentDescription.nonIdentifyingAttributes(),
160177
keyValuePair("os.family", runtime.GOOS),
161178
keyValuePair("host.name", hostname),
162-
},
179+
),
180+
}
181+
}
182+
183+
func (ad *AgentDescription) nonIdentifyingAttributes() []*protobufs.KeyValue {
184+
toReturn := make([]*protobufs.KeyValue, len(ad.NonIdentifyingAttributes))
185+
i := 0
186+
for k, v := range ad.NonIdentifyingAttributes {
187+
toReturn[i] = keyValuePair(k, v)
188+
i++
163189
}
190+
return toReturn
164191
}
165192

166193
func keyValuePair(key string, value string) *protobufs.KeyValue {
@@ -174,7 +201,7 @@ func keyValuePair(key string, value string) *protobufs.KeyValue {
174201
}
175202
}
176203

177-
func (c *Config) GetNewInstanceId() uuid.UUID {
204+
func mustGetInstanceId() uuid.UUID {
178205
u, err := uuid.NewV7()
179206
if err != nil {
180207
// This really should never happen and if it does we should fail.
@@ -183,6 +210,11 @@ func (c *Config) GetNewInstanceId() uuid.UUID {
183210
return u
184211
}
185212

213+
func (c *Config) GetNewInstanceId() uuid.UUID {
214+
c.instanceId = mustGetInstanceId()
215+
return c.instanceId
216+
}
217+
186218
func (c *Config) RemoteConfigEnabled() bool {
187219
capabilities := c.GetCapabilities()
188220
return capabilities&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0

cmd/operator-opamp-bridge/config/config_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ import (
1010
"time"
1111

1212
"github.com/go-logr/logr"
13+
"github.com/google/uuid"
14+
"github.com/open-telemetry/opamp-go/protobufs"
1315
"github.com/stretchr/testify/assert"
16+
"github.com/stretchr/testify/require"
1417
)
1518

1619
func TestLoad(t *testing.T) {
1720
type args struct {
1821
file string
1922
envVariables map[string]string
2023
}
24+
instanceId := uuid.New()
2125
tests := []struct {
2226
name string
2327
args args
@@ -30,6 +34,7 @@ func TestLoad(t *testing.T) {
3034
file: "./testdata/agent.yaml",
3135
},
3236
want: &Config{
37+
instanceId: instanceId,
3338
RootLogger: logr.Discard(),
3439
Endpoint: "ws://127.0.0.1:4320/v1/opamp",
3540
Capabilities: map[Capability]bool{
@@ -55,6 +60,7 @@ func TestLoad(t *testing.T) {
5560
file: "./testdata/agenthttpbasic.yaml",
5661
},
5762
want: &Config{
63+
instanceId: instanceId,
5864
RootLogger: logr.Discard(),
5965
Endpoint: "http://127.0.0.1:4320/v1/opamp",
6066
HeartbeatInterval: 45 * time.Second,
@@ -82,6 +88,7 @@ func TestLoad(t *testing.T) {
8288
file: "./testdata/agentbasiccomponentsallowed.yaml",
8389
},
8490
want: &Config{
91+
instanceId: instanceId,
8592
RootLogger: logr.Discard(),
8693
Endpoint: "ws://127.0.0.1:4320/v1/opamp",
8794
Capabilities: map[Capability]bool{
@@ -133,6 +140,7 @@ func TestLoad(t *testing.T) {
133140
},
134141
},
135142
want: &Config{
143+
instanceId: instanceId,
136144
RootLogger: logr.Discard(),
137145
Endpoint: "ws://127.0.0.1:4320/v1/opamp",
138146
Headers: map[string]string{
@@ -158,6 +166,41 @@ func TestLoad(t *testing.T) {
158166
},
159167
wantErr: assert.NoError,
160168
},
169+
{
170+
name: "base case with nonidentify attributes",
171+
args: args{
172+
file: "./testdata/agentwithdescription.yaml",
173+
envVariables: map[string]string{
174+
"MY_ENV_VAR_1": "my-env-variable-1-value",
175+
"MY_ENV_VAR_2": "my-env-variable-2-value",
176+
},
177+
},
178+
want: &Config{
179+
instanceId: instanceId,
180+
RootLogger: logr.Discard(),
181+
Endpoint: "ws://127.0.0.1:4320/v1/opamp",
182+
AgentDescription: AgentDescription{
183+
NonIdentifyingAttributes: map[string]string{
184+
"custom.attribute": "custom-value",
185+
},
186+
},
187+
Capabilities: map[Capability]bool{
188+
AcceptsRemoteConfig: true,
189+
ReportsEffectiveConfig: true,
190+
ReportsOwnTraces: true,
191+
ReportsOwnMetrics: true,
192+
ReportsOwnLogs: true,
193+
AcceptsOpAMPConnectionSettings: true,
194+
AcceptsOtherConnectionSettings: true,
195+
AcceptsRestartCommand: true,
196+
ReportsHealth: true,
197+
ReportsRemoteConfig: true,
198+
AcceptsPackages: false,
199+
ReportsPackageStatuses: false,
200+
},
201+
},
202+
wantErr: assert.NoError,
203+
},
161204
}
162205
for _, tt := range tests {
163206
t.Run(tt.name, func(t *testing.T) {
@@ -173,9 +216,41 @@ func TestLoad(t *testing.T) {
173216
return
174217
}
175218
// there are some fields we don't care about, so we ignore them.
219+
got.instanceId = tt.want.instanceId
176220
got.ClusterConfig = tt.want.ClusterConfig
177221
got.RootLogger = tt.want.RootLogger
178222
assert.Equalf(t, tt.want, got, "Load(%v)", tt.args.file)
179223
})
180224
}
181225
}
226+
227+
func TestGetDescription(t *testing.T) {
228+
got := NewConfig(logr.Discard())
229+
instanceId := uuid.New()
230+
got.instanceId = instanceId
231+
err := LoadFromFile(got, "./testdata/agentwithdescription.yaml")
232+
require.NoError(t, err, fmt.Sprintf("Load(%v)", "./testdata/agentwithdescription.yaml"))
233+
desc := got.GetDescription()
234+
assert.Len(t, desc.IdentifyingAttributes, 3)
235+
assert.Contains(t, desc.IdentifyingAttributes, &protobufs.KeyValue{Key: "service.instance.id", Value: &protobufs.AnyValue{
236+
Value: &protobufs.AnyValue_StringValue{StringValue: instanceId.String()},
237+
}})
238+
assert.Len(t, desc.NonIdentifyingAttributes, 3)
239+
assert.Contains(t, desc.NonIdentifyingAttributes, &protobufs.KeyValue{Key: "custom.attribute", Value: &protobufs.AnyValue{
240+
Value: &protobufs.AnyValue_StringValue{StringValue: "custom-value"},
241+
}})
242+
}
243+
244+
func TestGetDescriptionNoneSet(t *testing.T) {
245+
got := NewConfig(logr.Discard())
246+
instanceId := uuid.New()
247+
got.instanceId = instanceId
248+
err := LoadFromFile(got, "./testdata/agent.yaml")
249+
require.NoError(t, err, fmt.Sprintf("Load(%v)", "./testdata/agent.yaml"))
250+
desc := got.GetDescription()
251+
assert.Len(t, desc.IdentifyingAttributes, 3)
252+
assert.Contains(t, desc.IdentifyingAttributes, &protobufs.KeyValue{Key: "service.instance.id", Value: &protobufs.AnyValue{
253+
Value: &protobufs.AnyValue_StringValue{StringValue: instanceId.String()},
254+
}})
255+
assert.Len(t, desc.NonIdentifyingAttributes, 2)
256+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
endpoint: ws://127.0.0.1:4320/v1/opamp
2+
description:
3+
non_identifying_attributes:
4+
custom.attribute: "custom-value"
5+
capabilities:
6+
AcceptsRemoteConfig: true
7+
ReportsEffectiveConfig: true
8+
AcceptsPackages: false
9+
ReportsPackageStatuses: false
10+
ReportsOwnTraces: true
11+
ReportsOwnMetrics: true
12+
ReportsOwnLogs: true
13+
AcceptsOpAMPConnectionSettings: true
14+
AcceptsOtherConnectionSettings: true
15+
AcceptsRestartCommand: true
16+
ReportsHealth: true
17+
ReportsRemoteConfig: true

config/crd/bases/opentelemetry.io_opampbridges.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,15 @@ spec:
486486
type: string
487487
type: array
488488
type: object
489+
description:
490+
properties:
491+
non_identifying_attributes:
492+
additionalProperties:
493+
type: string
494+
type: object
495+
required:
496+
- non_identifying_attributes
497+
type: object
489498
endpoint:
490499
type: string
491500
env:

0 commit comments

Comments
 (0)