Skip to content

Commit b27d824

Browse files
author
Paulo Janotti
authored
Use strict mode to read config (#375)
* Use strict mode to read config Reading the config using exact matches causes errors for unknown sections reducing the changes of accidental errors on the configuration files. * Add comments and fix testbed config files
1 parent d769eb5 commit b27d824

30 files changed

+343
-190
lines changed

cmd/otelcol/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ func main() {
3434
handleErr(err)
3535

3636
svc := service.New(factories)
37-
err = svc.StartUnified()
37+
err = svc.Start()
3838
handleErr(err)
3939
}

config/config.go

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os"
2424
"strings"
2525

26+
"github.com/mitchellh/mapstructure"
2627
"github.com/spf13/viper"
2728
"go.uber.org/zap"
2829

@@ -58,9 +59,15 @@ const (
5859
errPipelineReceiverNotExists
5960
errPipelineProcessorNotExists
6061
errPipelineExporterNotExists
61-
errUnmarshalError
6262
errMissingReceivers
6363
errMissingExporters
64+
errUnmarshalErrorOnTopLevelSection
65+
errUnmarshalErrorOnExtension
66+
errUnmarshalErrorOnService
67+
errUnmarshalErrorOnReceiver
68+
errUnmarshalErrorOnProcessor
69+
errUnmarshalErrorOnExporter
70+
errUnmarshalErrorOnPipeline
6471
)
6572

6673
type configError struct {
@@ -123,6 +130,23 @@ func Load(
123130

124131
// Load the config.
125132

133+
// Struct to validate top level sections.
134+
var topLevelSections struct {
135+
Extensions map[string]interface{} `mapstructure:"extensions"`
136+
Service map[string]interface{} `mapstructure:"service"`
137+
Receivers map[string]interface{} `mapstructure:"receivers"`
138+
Processors map[string]interface{} `mapstructure:"processors"`
139+
Exporters map[string]interface{} `mapstructure:"exporters"`
140+
Pipelines map[string]interface{} `mapstructure:"pipelines"`
141+
}
142+
143+
if err := v.UnmarshalExact(&topLevelSections); err != nil {
144+
return nil, &configError{
145+
code: errUnmarshalErrorOnTopLevelSection,
146+
msg: fmt.Sprintf("error reading top level sections: %s", err.Error()),
147+
}
148+
}
149+
126150
// Start with extensions and service.
127151

128152
extensions, err := loadExtensions(v, factories.Extensions)
@@ -250,9 +274,9 @@ func loadExtensions(v *viper.Viper, factories map[string]extension.Factory) (con
250274

251275
// Now that the default config struct is created we can Unmarshal into it
252276
// and it will apply user-defined config on top of the default.
253-
if err := sv.Unmarshal(extensionCfg); err != nil {
277+
if err := sv.UnmarshalExact(extensionCfg); err != nil {
254278
return nil, &configError{
255-
code: errUnmarshalError,
279+
code: errUnmarshalErrorOnExtension,
256280
msg: fmt.Sprintf("error reading settings for extension type %q: %v", typeStr, err),
257281
}
258282
}
@@ -272,9 +296,9 @@ func loadExtensions(v *viper.Viper, factories map[string]extension.Factory) (con
272296

273297
func loadService(v *viper.Viper) (configmodels.Service, error) {
274298
var service configmodels.Service
275-
if err := v.UnmarshalKey(serviceKeyName, &service); err != nil {
299+
if err := v.UnmarshalKey(serviceKeyName, &service, errorOnUnused); err != nil {
276300
return service, &configError{
277-
code: errUnmarshalError,
301+
code: errUnmarshalErrorOnService,
278302
msg: fmt.Sprintf("error reading settings for %q: %v", serviceKeyName, err),
279303
}
280304
}
@@ -336,16 +360,12 @@ func loadReceivers(v *viper.Viper, factories map[string]receiver.Factory) (confi
336360
// This configuration requires a custom unmarshaler, use it.
337361
err = customUnmarshaler(subViper, key, receiverCfg)
338362
} else {
339-
// Standard viper unmarshaler is fine.
340-
// TODO(ccaraman): UnmarshallExact should be used to catch erroneous config entries.
341-
// This leads to quickly identifying config values that are not supported and reduce confusion for
342-
// users.
343-
err = sv.Unmarshal(receiverCfg)
363+
err = sv.UnmarshalExact(receiverCfg)
344364
}
345365

346366
if err != nil {
347367
return nil, &configError{
348-
code: errUnmarshalError,
368+
code: errUnmarshalErrorOnReceiver,
349369
msg: fmt.Sprintf("error reading settings for receiver type %q: %v", typeStr, err),
350370
}
351371
}
@@ -410,9 +430,9 @@ func loadExporters(v *viper.Viper, factories map[string]exporter.Factory) (confi
410430

411431
// Now that the default config struct is created we can Unmarshal into it
412432
// and it will apply user-defined config on top of the default.
413-
if err := sv.Unmarshal(exporterCfg); err != nil {
433+
if err := sv.UnmarshalExact(exporterCfg); err != nil {
414434
return nil, &configError{
415-
code: errUnmarshalError,
435+
code: errUnmarshalErrorOnExporter,
416436
msg: fmt.Sprintf("error reading settings for exporter type %q: %v", typeStr, err),
417437
}
418438
}
@@ -470,9 +490,9 @@ func loadProcessors(v *viper.Viper, factories map[string]processor.Factory) (con
470490

471491
// Now that the default config struct is created we can Unmarshal into it
472492
// and it will apply user-defined config on top of the default.
473-
if err := sv.Unmarshal(processorCfg); err != nil {
493+
if err := sv.UnmarshalExact(processorCfg); err != nil {
474494
return nil, &configError{
475-
code: errUnmarshalError,
495+
code: errUnmarshalErrorOnProcessor,
476496
msg: fmt.Sprintf("error reading settings for processor type %q: %v", typeStr, err),
477497
}
478498
}
@@ -529,9 +549,9 @@ func loadPipelines(v *viper.Viper) (configmodels.Pipelines, error) {
529549

530550
// Now that the default config struct is created we can Unmarshal into it
531551
// and it will apply user-defined config on top of the default.
532-
if err := subViper.UnmarshalKey(key, &pipelineCfg); err != nil {
552+
if err := subViper.UnmarshalKey(key, &pipelineCfg, errorOnUnused); err != nil {
533553
return nil, &configError{
534-
code: errUnmarshalError,
554+
code: errUnmarshalErrorOnPipeline,
535555
msg: fmt.Sprintf("error reading settings for pipeline type %q: %v", typeStr, err),
536556
}
537557
}
@@ -876,3 +896,12 @@ func expandStringValues(value interface{}) interface{} {
876896
return nmap
877897
}
878898
}
899+
900+
// errorOnUnused sets the decoder configuration to error in case of unused sections
901+
// are present in the configuration.
902+
func errorOnUnused(decoderCfg *mapstructure.DecoderConfig) {
903+
// If ErrorUnused is true, then it is an error for there to exist
904+
// keys in the original map that were unused in the decoding process
905+
// (extra keys).
906+
decoderCfg.ErrorUnused = true
907+
}

config/config_test.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -371,19 +371,26 @@ func TestDecodeConfig_Invalid(t *testing.T) {
371371
{name: "unknown-receiver-type", expected: errUnknownReceiverType},
372372
{name: "unknown-exporter-type", expected: errUnknownExporterType},
373373
{name: "unknown-processor-type", expected: errUnknownProcessorType},
374-
{name: "invalid-extension-disabled-value", expected: errUnmarshalError},
375-
{name: "invalid-service-extensions-value", expected: errUnmarshalError},
376-
{name: "invalid-bool-value", expected: errUnmarshalError},
377-
{name: "invalid-sequence-value", expected: errUnmarshalError},
378-
{name: "invalid-disabled-bool-value", expected: errUnmarshalError},
379-
{name: "invalid-disabled-bool-value2", expected: errUnmarshalError},
374+
{name: "invalid-extension-disabled-value", expected: errUnmarshalErrorOnExtension},
375+
{name: "invalid-service-extensions-value", expected: errUnmarshalErrorOnService},
376+
{name: "invalid-bool-value", expected: errUnmarshalErrorOnProcessor},
377+
{name: "invalid-sequence-value", expected: errUnmarshalErrorOnPipeline},
378+
{name: "invalid-disabled-bool-value", expected: errUnmarshalErrorOnExporter},
379+
{name: "invalid-disabled-bool-value2", expected: errUnmarshalErrorOnReceiver},
380380
{name: "invalid-pipeline-type", expected: errInvalidPipelineType},
381381
{name: "invalid-pipeline-type-and-name", expected: errInvalidTypeAndNameKey},
382382
{name: "duplicate-extension", expected: errDuplicateExtensionName},
383383
{name: "duplicate-receiver", expected: errDuplicateReceiverName},
384384
{name: "duplicate-exporter", expected: errDuplicateExporterName},
385385
{name: "duplicate-processor", expected: errDuplicateProcessorName},
386386
{name: "duplicate-pipeline", expected: errDuplicatePipelineName},
387+
{name: "invalid-top-level-section", expected: errUnmarshalErrorOnTopLevelSection},
388+
{name: "invalid-extension-section", expected: errUnmarshalErrorOnExtension},
389+
{name: "invalid-service-section", expected: errUnmarshalErrorOnService},
390+
{name: "invalid-receiver-section", expected: errUnmarshalErrorOnReceiver},
391+
{name: "invalid-processor-section", expected: errUnmarshalErrorOnProcessor},
392+
{name: "invalid-exporter-section", expected: errUnmarshalErrorOnExporter},
393+
{name: "invalid-pipeline-section", expected: errUnmarshalErrorOnPipeline},
387394
}
388395

389396
factories, err := ExampleComponents()
@@ -400,8 +407,8 @@ func TestDecodeConfig_Invalid(t *testing.T) {
400407
test.expected, err, test.name)
401408
} else {
402409
if cfgErr.code != test.expected {
403-
t.Errorf("expected config error code %v but got error code %v on invalid config case: %s",
404-
test.expected, cfgErr.code, test.name)
410+
t.Errorf("expected config error code %v but got error code %v (msg: %q) on invalid config case: %s",
411+
test.expected, cfgErr.code, cfgErr.msg, test.name)
405412
}
406413

407414
if cfgErr.Error() == "" {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
receivers:
2+
examplereceiver:
3+
processors:
4+
exampleprocessor:
5+
exporters:
6+
exampleexporter:
7+
unknown_section: exporter
8+
extensions:
9+
exampleextension:
10+
service:
11+
extensions:
12+
- exampleextension
13+
pipelines:
14+
traces:
15+
receivers:
16+
- examplereceiver
17+
processors:
18+
- exampleprocessor
19+
exporters:
20+
- exampleexporter
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
receivers:
2+
examplereceiver:
3+
processors:
4+
exampleprocessor:
5+
exporters:
6+
exampleexporter:
7+
extensions:
8+
exampleextension:
9+
unknown_section:
10+
a_num: 2
11+
service:
12+
extensions:
13+
- examapleextension
14+
pipelines:
15+
traces:
16+
receivers:
17+
- examplereceiver
18+
processors:
19+
- exampleprocessor
20+
exporters:
21+
- exampleexporter
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
receivers:
2+
examplereceiver:
3+
processors:
4+
exampleprocessor:
5+
exporters:
6+
exampleexporter:
7+
extensions:
8+
exampleextension:
9+
service:
10+
extensions:
11+
- exampleextension
12+
pipelines:
13+
traces:
14+
receivers:
15+
- examplereceiver
16+
processors:
17+
- exampleprocessor
18+
exporters:
19+
- exampleexporter
20+
unknown_section: 1
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
receivers:
2+
examplereceiver:
3+
processors:
4+
exampleprocessor:
5+
unknown_section:
6+
a_num: 2
7+
exporters:
8+
exampleexporter:
9+
extensions:
10+
exampleextension:
11+
service:
12+
extensions:
13+
- examapleextension
14+
pipelines:
15+
traces:
16+
receivers:
17+
- examplereceiver
18+
processors:
19+
- exampleprocessor
20+
exporters:
21+
- exampleexporter
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
receivers:
2+
examplereceiver:
3+
unknown_section:
4+
a_num: 2
5+
processors:
6+
exampleprocessor:
7+
exporters:
8+
exampleexporter:
9+
extensions:
10+
exampleextension:
11+
service:
12+
extensions:
13+
- examapleextension
14+
pipelines:
15+
traces:
16+
receivers:
17+
- examplereceiver
18+
processors:
19+
- exampleprocessor
20+
exporters:
21+
- exampleexporter
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
receivers:
2+
examplereceiver:
3+
processors:
4+
exampleprocessor:
5+
exporters:
6+
exampleexporter:
7+
extensions:
8+
exampleextension:
9+
service:
10+
extenstions:
11+
- examapleextension
12+
unknown_section:
13+
a_num: 2
14+
pipelines:
15+
traces:
16+
receivers:
17+
- examplereceiver
18+
processors:
19+
- exampleprocessor
20+
exporters:
21+
- exampleexporter
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
receivers:
2+
examplereceiver:
3+
processors:
4+
exampleprocessor:
5+
exporters:
6+
exampleexporter:
7+
extensions:
8+
exampleextension:
9+
service:
10+
extenstions:
11+
- examapleextension
12+
pipelines:
13+
traces:
14+
receivers:
15+
- examplereceiver
16+
processors:
17+
- exampleprocessor
18+
exporters:
19+
- exampleexporter
20+
unknown_section:
21+
a_num: 2

0 commit comments

Comments
 (0)