Skip to content

Commit 5a11b48

Browse files
authored
v1beta1: apply telemetry config defaults in webhook (#3361)
Signed-off-by: Benedikt Bongartz <[email protected]> Update .chloggen/default_telemetry_settings.yaml add another webhook test Signed-off-by: Benedikt Bongartz <[email protected]> avoid using mapstructure Signed-off-by: Benedikt Bongartz <[email protected]> test: assert on addr
1 parent 01b0755 commit 5a11b48

File tree

23 files changed

+253
-30
lines changed

23 files changed

+253
-30
lines changed
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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: collector
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Expose the Collector telemetry endpoint by default.
9+
10+
# One or more tracking issues related to the change
11+
issues: [3361]
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: |
17+
The collector v0.111.0 changes the default binding of the telemetry metrics endpoint from `0.0.0.0` to `localhost`.
18+
To avoid any disruption we fallback to "0.0.0.0:{PORT}" as default address.
19+
Details can be found here: [opentelemetry-collector#11251](https://github.com/open-telemetry/opentelemetry-collector/pull/11251)

apis/v1beta1/collector_webhook_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
169169
Mode: v1beta1.ModeDeployment,
170170
UpgradeStrategy: v1beta1.UpgradeStrategyAutomatic,
171171
Config: func() v1beta1.Config {
172-
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"endpoint":"0.0.0.0:4317"},"http":{"endpoint":"0.0.0.0:4318"}}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
172+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"endpoint":"0.0.0.0:4317"},"http":{"endpoint":"0.0.0.0:4318"}}}},"exporters":{"debug":null},"service":{"telemetry":{"metrics":{"address":"0.0.0.0:8888"}},"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
173173
var cfg v1beta1.Config
174174
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
175175
return cfg
@@ -182,7 +182,7 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
182182
otelcol: v1beta1.OpenTelemetryCollector{
183183
Spec: v1beta1.OpenTelemetryCollectorSpec{
184184
Config: func() v1beta1.Config {
185-
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"headers":{"example":"another"}},"http":{"endpoint":"0.0.0.0:4000"}}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
185+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"headers":{"example":"another"}},"http":{"endpoint":"0.0.0.0:4000"}}}},"exporters":{"debug":null},"service":{"telemetry":{"metrics":{"address":"1.2.3.4:7654"}},"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
186186
var cfg v1beta1.Config
187187
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
188188
return cfg
@@ -201,7 +201,7 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
201201
Mode: v1beta1.ModeDeployment,
202202
UpgradeStrategy: v1beta1.UpgradeStrategyAutomatic,
203203
Config: func() v1beta1.Config {
204-
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"endpoint":"0.0.0.0:4317","headers":{"example":"another"}},"http":{"endpoint":"0.0.0.0:4000"}}}},"exporters":{"debug":null},"service":{"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
204+
const input = `{"receivers":{"otlp":{"protocols":{"grpc":{"endpoint":"0.0.0.0:4317","headers":{"example":"another"}},"http":{"endpoint":"0.0.0.0:4000"}}}},"exporters":{"debug":null},"service":{"telemetry":{"metrics":{"address":"1.2.3.4:7654"}},"pipelines":{"traces":{"receivers":["otlp"],"exporters":["debug"]}}}}`
205205
var cfg v1beta1.Config
206206
require.NoError(t, yaml.Unmarshal([]byte(input), &cfg))
207207
return cfg
@@ -554,6 +554,9 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
554554
)
555555
ctx := context.Background()
556556
err := cvw.Default(ctx, &test.otelcol)
557+
if test.expected.Spec.Config.Service.Telemetry == nil {
558+
assert.NoError(t, test.expected.Spec.Config.Service.ApplyDefaults(), "could not apply defaults")
559+
}
557560
assert.NoError(t, err)
558561
assert.Equal(t, test.expected, test.otelcol)
559562
})

apis/v1beta1/config.go

+42-8
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ func (c *Config) getPortsForComponentKinds(logger logr.Logger, componentKinds ..
228228

229229
// applyDefaultForComponentKinds applies defaults to the endpoints for the given ComponentKind(s).
230230
func (c *Config) applyDefaultForComponentKinds(logger logr.Logger, componentKinds ...ComponentKind) error {
231+
if err := c.Service.ApplyDefaults(); err != nil {
232+
return err
233+
}
231234
enabledComponents := c.GetEnabledComponents()
232235
for _, componentKind := range componentKinds {
233236
var retriever components.ParserRetriever
@@ -371,24 +374,55 @@ type Service struct {
371374
Pipelines map[string]*Pipeline `json:"pipelines" yaml:"pipelines"`
372375
}
373376

374-
// MetricsPort gets the port number for the metrics endpoint from the collector config if it has been set.
375-
func (s *Service) MetricsPort() (int32, error) {
377+
// MetricsEndpoint gets the port number and host address for the metrics endpoint from the collector config if it has been set.
378+
func (s *Service) MetricsEndpoint() (string, int32, error) {
379+
defaultAddr := "0.0.0.0"
376380
if s.GetTelemetry() == nil {
377381
// telemetry isn't set, use the default
378-
return 8888, nil
382+
return defaultAddr, 8888, nil
379383
}
380-
_, port, netErr := net.SplitHostPort(s.GetTelemetry().Metrics.Address)
384+
host, port, netErr := net.SplitHostPort(s.GetTelemetry().Metrics.Address)
381385
if netErr != nil && strings.Contains(netErr.Error(), "missing port in address") {
382-
return 8888, nil
386+
return defaultAddr, 8888, nil
383387
} else if netErr != nil {
384-
return 0, netErr
388+
return "", 0, netErr
385389
}
386390
i64, err := strconv.ParseInt(port, 10, 32)
387391
if err != nil {
388-
return 0, err
392+
return "", 0, err
393+
}
394+
395+
if host == "" {
396+
host = defaultAddr
397+
}
398+
399+
return host, int32(i64), nil
400+
}
401+
402+
// ApplyDefaults inserts configuration defaults if it has not been set.
403+
func (s *Service) ApplyDefaults() error {
404+
telemetryAddr, telemetryPort, err := s.MetricsEndpoint()
405+
if err != nil {
406+
return err
407+
}
408+
tm := &AnyConfig{
409+
Object: map[string]interface{}{
410+
"metrics": map[string]interface{}{
411+
"address": fmt.Sprintf("%s:%d", telemetryAddr, telemetryPort),
412+
},
413+
},
389414
}
390415

391-
return int32(i64), nil
416+
if s.Telemetry == nil {
417+
s.Telemetry = tm
418+
return nil
419+
}
420+
// NOTE: Merge without overwrite. If a telemetry endpoint is specified, the defaulting
421+
// respects the configuration and returns an equal value.
422+
if err := mergo.Merge(s.Telemetry, tm); err != nil {
423+
return fmt.Errorf("telemetry config merge failed: %w", err)
424+
}
425+
return nil
392426
}
393427

394428
// MetricsConfig comes from the collector.

apis/v1beta1/config_test.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,13 @@ func TestConfigToMetricsPort(t *testing.T) {
220220

221221
for _, tt := range []struct {
222222
desc string
223+
expectedAddr string
223224
expectedPort int32
224225
config Service
225226
}{
226227
{
227228
"custom port",
229+
"0.0.0.0",
228230
9090,
229231
Service{
230232
Telemetry: &AnyConfig{
@@ -238,6 +240,7 @@ func TestConfigToMetricsPort(t *testing.T) {
238240
},
239241
{
240242
"bad address",
243+
"0.0.0.0",
241244
8888,
242245
Service{
243246
Telemetry: &AnyConfig{
@@ -251,6 +254,7 @@ func TestConfigToMetricsPort(t *testing.T) {
251254
},
252255
{
253256
"missing address",
257+
"0.0.0.0",
254258
8888,
255259
Service{
256260
Telemetry: &AnyConfig{
@@ -264,21 +268,38 @@ func TestConfigToMetricsPort(t *testing.T) {
264268
},
265269
{
266270
"missing metrics",
271+
"0.0.0.0",
267272
8888,
268273
Service{
269274
Telemetry: &AnyConfig{},
270275
},
271276
},
272277
{
273278
"missing telemetry",
279+
"0.0.0.0",
274280
8888,
275281
Service{},
276282
},
283+
{
284+
"configured telemetry",
285+
"1.2.3.4",
286+
4567,
287+
Service{
288+
Telemetry: &AnyConfig{
289+
Object: map[string]interface{}{
290+
"metrics": map[string]interface{}{
291+
"address": "1.2.3.4:4567",
292+
},
293+
},
294+
},
295+
},
296+
},
277297
} {
278298
t.Run(tt.desc, func(t *testing.T) {
279299
// these are acceptable failures, we return to the collector's default metric port
280-
port, err := tt.config.MetricsPort()
300+
addr, port, err := tt.config.MetricsEndpoint()
281301
assert.NoError(t, err)
302+
assert.Equal(t, tt.expectedAddr, addr)
282303
assert.Equal(t, tt.expectedPort, port)
283304
})
284305
}

internal/manifests/collector/container.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func getConfigContainerPorts(logger logr.Logger, conf v1beta1.Config) (map[strin
223223
}
224224
}
225225

226-
metricsPort, err := conf.Service.MetricsPort()
226+
_, metricsPort, err := conf.Service.MetricsEndpoint()
227227
if err != nil {
228228
logger.Info("couldn't determine metrics port from configuration, using 8888 default value", "error", err)
229229
metricsPort = 8888

internal/manifests/collector/service.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func MonitoringService(params manifests.Params) (*corev1.Service, error) {
8383
return nil, err
8484
}
8585

86-
metricsPort, err := params.OtelCol.Spec.Config.Service.MetricsPort()
86+
_, metricsPort, err := params.OtelCol.Spec.Config.Service.MetricsEndpoint()
8787
if err != nil {
8888
return nil, err
8989
}

pkg/collector/upgrade/upgrade_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func TestEnvVarUpdates(t *testing.T) {
141141
require.Equal(t, collectorInstance.Status.Version, persisted.Status.Version)
142142

143143
currentV := version.Get()
144-
currentV.OpenTelemetryCollector = "0.110.0"
144+
currentV.OpenTelemetryCollector = "0.111.0"
145145
up := &upgrade.VersionUpgrade{
146146
Log: logger,
147147
Version: currentV,

pkg/collector/upgrade/v0_111_0.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package upgrade
16+
17+
import (
18+
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
19+
)
20+
21+
func upgrade0_111_0(_ VersionUpgrade, otelcol *v1beta1.OpenTelemetryCollector) (*v1beta1.OpenTelemetryCollector, error) { //nolint:unparam
22+
return otelcol, otelcol.Spec.Config.Service.ApplyDefaults()
23+
}
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package upgrade_test
16+
17+
import (
18+
"context"
19+
"testing"
20+
21+
"github.com/stretchr/testify/assert"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/client-go/tools/record"
24+
25+
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
26+
"github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade"
27+
)
28+
29+
func Test0_111_0Upgrade(t *testing.T) {
30+
31+
defaultCollector := v1beta1.OpenTelemetryCollector{
32+
ObjectMeta: metav1.ObjectMeta{
33+
Name: "otel-my-instance",
34+
Namespace: "somewhere",
35+
},
36+
Status: v1beta1.OpenTelemetryCollectorStatus{
37+
Version: "0.110.0",
38+
},
39+
Spec: v1beta1.OpenTelemetryCollectorSpec{
40+
OpenTelemetryCommonFields: v1beta1.OpenTelemetryCommonFields{},
41+
Config: v1beta1.Config{},
42+
},
43+
}
44+
45+
defaultCollectorWithConfig := defaultCollector.DeepCopy()
46+
47+
defaultCollectorWithConfig.Spec.Config.Service.Telemetry = &v1beta1.AnyConfig{
48+
Object: map[string]interface{}{
49+
"metrics": map[string]interface{}{
50+
"address": "1.2.3.4:8888",
51+
},
52+
},
53+
}
54+
55+
tt := []struct {
56+
name string
57+
input v1beta1.OpenTelemetryCollector
58+
expected v1beta1.OpenTelemetryCollector
59+
}{
60+
{
61+
name: "telemetry settings exist",
62+
input: *defaultCollectorWithConfig,
63+
expected: *defaultCollectorWithConfig,
64+
},
65+
{
66+
name: "telemetry settings do not exist",
67+
input: *defaultCollector.DeepCopy(),
68+
expected: func() v1beta1.OpenTelemetryCollector {
69+
col := defaultCollector.DeepCopy()
70+
col.Spec.Config.Service.Telemetry = &v1beta1.AnyConfig{
71+
Object: map[string]interface{}{
72+
"metrics": map[string]interface{}{
73+
"address": "0.0.0.0:8888",
74+
},
75+
},
76+
}
77+
return *col
78+
}(),
79+
},
80+
}
81+
82+
versionUpgrade := &upgrade.VersionUpgrade{
83+
Log: logger,
84+
Version: makeVersion("0.111.0"),
85+
Client: k8sClient,
86+
Recorder: record.NewFakeRecorder(upgrade.RecordBufferSize),
87+
}
88+
89+
for _, tc := range tt {
90+
t.Run(tc.name, func(t *testing.T) {
91+
col, err := versionUpgrade.ManagedInstance(context.Background(), tc.input)
92+
if err != nil {
93+
t.Errorf("expect err: nil but got: %v", err)
94+
}
95+
assert.Equal(t, tc.expected.Spec.Config.Service.Telemetry, col.Spec.Config.Service.Telemetry)
96+
})
97+
}
98+
}

pkg/collector/upgrade/versions.go

+4
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ var (
106106
Version: *semver.MustParse("0.110.0"),
107107
upgradeV1beta1: upgrade0_110_0,
108108
},
109+
{
110+
Version: *semver.MustParse("0.111.0"),
111+
upgradeV1beta1: upgrade0_111_0,
112+
},
109113
}
110114

111115
// Latest represents the latest version that we need to upgrade. This is not necessarily the latest known version.

tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer-go/02-assert.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ spec:
9292
exporters:
9393
debug: null
9494
service:
95+
telemetry:
96+
metrics:
97+
address: 0.0.0.0:8888
9598
pipelines:
9699
traces:
97100
exporters:

tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ spec:
331331
exporters:
332332
debug: null
333333
service:
334+
telemetry:
335+
metrics:
336+
address: 0.0.0.0:8888
334337
pipelines:
335338
traces:
336339
exporters:

0 commit comments

Comments
 (0)