Skip to content

Commit df6f8fd

Browse files
authored
[otelcol] Allow passing confmap.Providers to otelcol.Collector (#9228)
**Description:** One way to work toward #4759. This implements the second approach that I've outlined here: #4759 (comment). I think the main advantage of this approach is that it cleans up the API for the package. `otelcol.ConfigProvider` is a fairly thin wrapper around `confmap.Resolver`, so I think we could ultimately remove the interface, and any custom functionality for config merging or unmarshaling could be exposed to users through settings rather through a custom implementation. **Link to tracking Issue:** Works toward #4759 **Testing:** Unit tests **Documentation:** Added Godoc comments.
1 parent 62050a2 commit df6f8fd

6 files changed

+205
-102
lines changed

.chloggen/pass-confmap-providers.yaml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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. otlpreceiver)
7+
component: otelcol
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add `ConfigProviderSettings` to `CollectorSettings`
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [4759]
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: This allows passing a custom list of `confmap.Provider`s to `otelcol.NewCommand`.
19+
20+
# Optional: The change log or logs in which this entry should be included.
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users.
23+
# Include 'api' if there is a change to a library API.
24+
# Default: '[user]'
25+
change_logs: []

otelcol/collector.go

+22-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package otelcol // import "go.opentelemetry.io/collector/otelcol"
77

88
import (
99
"context"
10-
"errors"
1110
"fmt"
1211
"os"
1312
"os/signal"
@@ -66,10 +65,16 @@ type CollectorSettings struct {
6665
// and manually handle the signals to shutdown the collector.
6766
DisableGracefulShutdown bool
6867

68+
// Deprecated: [v0.95.0] Use ConfigProviderSettings instead.
6969
// ConfigProvider provides the service configuration.
7070
// If the provider watches for configuration change, collector may reload the new configuration upon changes.
7171
ConfigProvider ConfigProvider
7272

73+
// ConfigProviderSettings allows configuring the way the Collector retrieves its configuration
74+
// The Collector will reload based on configuration changes from the ConfigProvider if any
75+
// confmap.Providers watch for configuration changes.
76+
ConfigProviderSettings ConfigProviderSettings
77+
7378
// LoggingOptions provides a way to change behavior of zap logging.
7479
LoggingOptions []zap.Option
7580

@@ -92,6 +97,8 @@ type CollectorSettings struct {
9297
type Collector struct {
9398
set CollectorSettings
9499

100+
configProvider ConfigProvider
101+
95102
service *service.Service
96103
state *atomic.Int32
97104

@@ -105,8 +112,14 @@ type Collector struct {
105112

106113
// NewCollector creates and returns a new instance of Collector.
107114
func NewCollector(set CollectorSettings) (*Collector, error) {
108-
if set.ConfigProvider == nil {
109-
return nil, errors.New("invalid nil config provider")
115+
var err error
116+
configProvider := set.ConfigProvider
117+
118+
if configProvider == nil {
119+
configProvider, err = NewConfigProvider(set.ConfigProviderSettings)
120+
if err != nil {
121+
return nil, err
122+
}
110123
}
111124

112125
state := &atomic.Int32{}
@@ -119,6 +132,7 @@ func NewCollector(set CollectorSettings) (*Collector, error) {
119132
// the number of signals getting notified on is recommended.
120133
signalsChannel: make(chan os.Signal, 3),
121134
asyncErrorChannel: make(chan error),
135+
configProvider: configProvider,
122136
}, nil
123137
}
124138

@@ -146,7 +160,7 @@ func (col *Collector) setupConfigurationComponents(ctx context.Context) error {
146160

147161
var conf *confmap.Conf
148162

149-
if cp, ok := col.set.ConfigProvider.(ConfmapProvider); ok {
163+
if cp, ok := col.configProvider.(ConfmapProvider); ok {
150164
var err error
151165
conf, err = cp.GetConfmap(ctx)
152166

@@ -159,7 +173,7 @@ func (col *Collector) setupConfigurationComponents(ctx context.Context) error {
159173
if err != nil {
160174
return fmt.Errorf("failed to initialize factories: %w", err)
161175
}
162-
cfg, err := col.set.ConfigProvider.Get(ctx, factories)
176+
cfg, err := col.configProvider.Get(ctx, factories)
163177
if err != nil {
164178
return fmt.Errorf("failed to get config: %w", err)
165179
}
@@ -215,7 +229,7 @@ func (col *Collector) DryRun(ctx context.Context) error {
215229
if err != nil {
216230
return fmt.Errorf("failed to initialize factories: %w", err)
217231
}
218-
cfg, err := col.set.ConfigProvider.Get(ctx, factories)
232+
cfg, err := col.configProvider.Get(ctx, factories)
219233
if err != nil {
220234
return fmt.Errorf("failed to get config: %w", err)
221235
}
@@ -243,7 +257,7 @@ func (col *Collector) Run(ctx context.Context) error {
243257
LOOP:
244258
for {
245259
select {
246-
case err := <-col.set.ConfigProvider.Watch():
260+
case err := <-col.configProvider.Watch():
247261
if err != nil {
248262
col.service.Logger().Error("Config watch failed", zap.Error(err))
249263
break LOOP
@@ -280,7 +294,7 @@ func (col *Collector) shutdown(ctx context.Context) error {
280294
// Accumulate errors and proceed with shutting down remaining components.
281295
var errs error
282296

283-
if err := col.set.ConfigProvider.Shutdown(ctx); err != nil {
297+
if err := col.configProvider.Shutdown(ctx); err != nil {
284298
errs = multierr.Append(errs, fmt.Errorf("failed to shutdown config provider: %w", err))
285299
}
286300

otelcol/collector_test.go

+76-78
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818

1919
"go.opentelemetry.io/collector/component"
2020
"go.opentelemetry.io/collector/confmap"
21-
"go.opentelemetry.io/collector/confmap/converter/expandconverter"
2221
"go.opentelemetry.io/collector/extension/extensiontest"
2322
"go.opentelemetry.io/collector/processor/processortest"
2423
)
@@ -32,13 +31,10 @@ func TestStateString(t *testing.T) {
3231
}
3332

3433
func TestCollectorStartAsGoRoutine(t *testing.T) {
35-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}))
36-
require.NoError(t, err)
37-
3834
set := CollectorSettings{
39-
BuildInfo: component.NewDefaultBuildInfo(),
40-
Factories: nopFactories,
41-
ConfigProvider: cfgProvider,
35+
BuildInfo: component.NewDefaultBuildInfo(),
36+
Factories: nopFactories,
37+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}),
4238
}
4339
col, err := NewCollector(set)
4440
require.NoError(t, err)
@@ -56,13 +52,10 @@ func TestCollectorStartAsGoRoutine(t *testing.T) {
5652
}
5753

5854
func TestCollectorCancelContext(t *testing.T) {
59-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}))
60-
require.NoError(t, err)
61-
6255
set := CollectorSettings{
63-
BuildInfo: component.NewDefaultBuildInfo(),
64-
Factories: nopFactories,
65-
ConfigProvider: cfgProvider,
56+
BuildInfo: component.NewDefaultBuildInfo(),
57+
Factories: nopFactories,
58+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}),
6659
}
6760
col, err := NewCollector(set)
6861
require.NoError(t, err)
@@ -119,13 +112,10 @@ func TestCollectorStateAfterConfigChange(t *testing.T) {
119112
}
120113

121114
func TestCollectorReportError(t *testing.T) {
122-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}))
123-
require.NoError(t, err)
124-
125115
col, err := NewCollector(CollectorSettings{
126-
BuildInfo: component.NewDefaultBuildInfo(),
127-
Factories: nopFactories,
128-
ConfigProvider: cfgProvider,
116+
BuildInfo: component.NewDefaultBuildInfo(),
117+
Factories: nopFactories,
118+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}),
129119
})
130120
require.NoError(t, err)
131121

@@ -235,13 +225,10 @@ func TestComponentStatusWatcher(t *testing.T) {
235225
}
236226

237227
func TestCollectorSendSignal(t *testing.T) {
238-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}))
239-
require.NoError(t, err)
240-
241228
col, err := NewCollector(CollectorSettings{
242-
BuildInfo: component.NewDefaultBuildInfo(),
243-
Factories: nopFactories,
244-
ConfigProvider: cfgProvider,
229+
BuildInfo: component.NewDefaultBuildInfo(),
230+
Factories: nopFactories,
231+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}),
245232
})
246233
require.NoError(t, err)
247234

@@ -266,13 +253,10 @@ func TestCollectorSendSignal(t *testing.T) {
266253
func TestCollectorFailedShutdown(t *testing.T) {
267254
t.Skip("This test was using telemetry shutdown failure, switch to use a component that errors on shutdown.")
268255

269-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}))
270-
require.NoError(t, err)
271-
272256
col, err := NewCollector(CollectorSettings{
273-
BuildInfo: component.NewDefaultBuildInfo(),
274-
Factories: nopFactories,
275-
ConfigProvider: cfgProvider,
257+
BuildInfo: component.NewDefaultBuildInfo(),
258+
Factories: nopFactories,
259+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}),
276260
})
277261
require.NoError(t, err)
278262

@@ -294,16 +278,49 @@ func TestCollectorFailedShutdown(t *testing.T) {
294278
}
295279

296280
func TestCollectorStartInvalidConfig(t *testing.T) {
297-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}))
281+
col, err := NewCollector(CollectorSettings{
282+
BuildInfo: component.NewDefaultBuildInfo(),
283+
Factories: nopFactories,
284+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}),
285+
})
298286
require.NoError(t, err)
287+
assert.Error(t, col.Run(context.Background()))
288+
}
289+
290+
func TestNewCollectorInvalidConfigProviderSettings(t *testing.T) {
291+
_, err := NewCollector(CollectorSettings{
292+
BuildInfo: component.NewDefaultBuildInfo(),
293+
Factories: nopFactories,
294+
ConfigProviderSettings: ConfigProviderSettings{},
295+
})
296+
require.Error(t, err)
297+
}
298+
299+
func TestNewCollectorUseConfig(t *testing.T) {
300+
set := newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")})
299301

300302
col, err := NewCollector(CollectorSettings{
301-
BuildInfo: component.NewDefaultBuildInfo(),
302-
Factories: nopFactories,
303-
ConfigProvider: cfgProvider,
303+
BuildInfo: component.NewDefaultBuildInfo(),
304+
Factories: nopFactories,
305+
ConfigProviderSettings: set,
304306
})
305307
require.NoError(t, err)
306-
assert.Error(t, col.Run(context.Background()))
308+
require.NotNil(t, col.configProvider)
309+
}
310+
311+
func TestNewCollectorValidatesResolverSettings(t *testing.T) {
312+
set := ConfigProviderSettings{
313+
ResolverSettings: confmap.ResolverSettings{
314+
URIs: []string{filepath.Join("testdata", "otelcol-nop.yaml")},
315+
},
316+
}
317+
318+
_, err := NewCollector(CollectorSettings{
319+
BuildInfo: component.NewDefaultBuildInfo(),
320+
Factories: nopFactories,
321+
ConfigProviderSettings: set,
322+
})
323+
require.Error(t, err)
307324
}
308325

309326
func TestCollectorStartWithTraceContextPropagation(t *testing.T) {
@@ -318,13 +335,10 @@ func TestCollectorStartWithTraceContextPropagation(t *testing.T) {
318335

319336
for _, tt := range tests {
320337
t.Run(tt.file, func(t *testing.T) {
321-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", tt.file)}))
322-
require.NoError(t, err)
323-
324338
set := CollectorSettings{
325-
BuildInfo: component.NewDefaultBuildInfo(),
326-
Factories: nopFactories,
327-
ConfigProvider: cfgProvider,
339+
BuildInfo: component.NewDefaultBuildInfo(),
340+
Factories: nopFactories,
341+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", tt.file)}),
328342
}
329343

330344
col, err := NewCollector(set)
@@ -353,13 +367,10 @@ func TestCollectorRun(t *testing.T) {
353367

354368
for _, tt := range tests {
355369
t.Run(tt.file, func(t *testing.T) {
356-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", tt.file)}))
357-
require.NoError(t, err)
358-
359370
set := CollectorSettings{
360-
BuildInfo: component.NewDefaultBuildInfo(),
361-
Factories: nopFactories,
362-
ConfigProvider: cfgProvider,
371+
BuildInfo: component.NewDefaultBuildInfo(),
372+
Factories: nopFactories,
373+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", tt.file)}),
363374
}
364375
col, err := NewCollector(set)
365376
require.NoError(t, err)
@@ -374,13 +385,10 @@ func TestCollectorRun(t *testing.T) {
374385
}
375386

376387
func TestCollectorShutdownBeforeRun(t *testing.T) {
377-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}))
378-
require.NoError(t, err)
379-
380388
set := CollectorSettings{
381-
BuildInfo: component.NewDefaultBuildInfo(),
382-
Factories: nopFactories,
383-
ConfigProvider: cfgProvider,
389+
BuildInfo: component.NewDefaultBuildInfo(),
390+
Factories: nopFactories,
391+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-nop.yaml")}),
384392
}
385393
col, err := NewCollector(set)
386394
require.NoError(t, err)
@@ -396,14 +404,11 @@ func TestCollectorShutdownBeforeRun(t *testing.T) {
396404
}
397405

398406
func TestCollectorClosedStateOnStartUpError(t *testing.T) {
399-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}))
400-
require.NoError(t, err)
401-
402407
// Load a bad config causing startup to fail
403408
set := CollectorSettings{
404-
BuildInfo: component.NewDefaultBuildInfo(),
405-
Factories: nopFactories,
406-
ConfigProvider: cfgProvider,
409+
BuildInfo: component.NewDefaultBuildInfo(),
410+
Factories: nopFactories,
411+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}),
407412
}
408413
col, err := NewCollector(set)
409414
require.NoError(t, err)
@@ -416,14 +421,11 @@ func TestCollectorClosedStateOnStartUpError(t *testing.T) {
416421
}
417422

418423
func TestCollectorDryRun(t *testing.T) {
419-
cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}))
420-
require.NoError(t, err)
421-
422424
// Load a bad config causing startup to fail
423425
set := CollectorSettings{
424-
BuildInfo: component.NewDefaultBuildInfo(),
425-
Factories: nopFactories,
426-
ConfigProvider: cfgProvider,
426+
BuildInfo: component.NewDefaultBuildInfo(),
427+
Factories: nopFactories,
428+
ConfigProviderSettings: newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}),
427429
}
428430
col, err := NewCollector(set)
429431
require.NoError(t, err)
@@ -432,19 +434,15 @@ func TestCollectorDryRun(t *testing.T) {
432434
}
433435

434436
func TestPassConfmapToServiceFailure(t *testing.T) {
435-
cfgProvider, err := NewConfigProvider(ConfigProviderSettings{
436-
ResolverSettings: confmap.ResolverSettings{
437-
URIs: []string{filepath.Join("testdata", "otelcol-invalid.yaml")},
438-
Providers: makeMapProvidersMap(newFailureProvider()),
439-
Converters: []confmap.Converter{expandconverter.New(confmap.ConverterSettings{})},
440-
},
441-
})
442-
require.NoError(t, err)
443-
444437
set := CollectorSettings{
445-
BuildInfo: component.NewDefaultBuildInfo(),
446-
Factories: nopFactories,
447-
ConfigProvider: cfgProvider,
438+
BuildInfo: component.NewDefaultBuildInfo(),
439+
Factories: nopFactories,
440+
ConfigProviderSettings: ConfigProviderSettings{
441+
ResolverSettings: confmap.ResolverSettings{
442+
URIs: []string{filepath.Join("testdata", "otelcol-invalid.yaml")},
443+
Providers: makeMapProvidersMap(newFailureProvider()),
444+
},
445+
},
448446
}
449447
col, err := NewCollector(set)
450448
require.NoError(t, err)

0 commit comments

Comments
 (0)