@@ -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
6673type 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
273297func 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+ }
0 commit comments