@@ -141,10 +141,6 @@ func sanitizeExpanded(a any, useOriginal bool) any {
141
141
return c
142
142
case []any :
143
143
var newSlice []any
144
- if m == nil {
145
- return newSlice
146
- }
147
- newSlice = []any {}
148
144
for _ , e := range m {
149
145
newSlice = append (newSlice , sanitizeExpanded (e , useOriginal ))
150
146
}
@@ -240,6 +236,7 @@ func decodeConfig(m *Conf, result any, errorUnused bool, skipTopLevelUnmarshaler
240
236
// after the main unmarshaler hook is called,
241
237
// we unmarshal the embedded structs if present to merge with the result:
242
238
unmarshalerEmbeddedStructsHookFunc (),
239
+ zeroSliceHookFunc (),
243
240
),
244
241
}
245
242
decoder , err := mapstructure .NewDecoder (dc )
@@ -534,6 +531,37 @@ type Marshaler interface {
534
531
Marshal (component * Conf ) error
535
532
}
536
533
534
+ // This hook is used to solve the issue: https://github.com/open-telemetry/opentelemetry-collector/issues/4001
535
+ // We adopt the suggestion provided in this issue: https://github.com/mitchellh/mapstructure/issues/74#issuecomment-279886492
536
+ // We should empty every slice before unmarshalling unless user provided slice is nil.
537
+ // Assume that we had a struct with a field of type slice called `keys`, which has default values of ["a", "b"]
538
+ //
539
+ // type Config struct {
540
+ // Keys []string `mapstructure:"keys"`
541
+ // }
542
+ //
543
+ // The configuration provided by users may have following cases
544
+ // 1. configuration have `keys` field and have a non-nil values for this key, the output should be overridden
545
+ // - for example, input is {"keys", ["c"]}, then output is Config{ Keys: ["c"]}
546
+ //
547
+ // 2. configuration have `keys` field and have an empty slice for this key, the output should be overridden by empty slices
548
+ // - for example, input is {"keys", []}, then output is Config{ Keys: []}
549
+ //
550
+ // 3. configuration have `keys` field and have nil value for this key, the output should be default config
551
+ // - for example, input is {"keys": nil}, then output is Config{ Keys: ["a", "b"]}
552
+ //
553
+ // 4. configuration have no `keys` field specified, the output should be default config
554
+ // - for example, input is {}, then output is Config{ Keys: ["a", "b"]}
555
+ func zeroSliceHookFunc () mapstructure.DecodeHookFuncValue {
556
+ return func (from reflect.Value , to reflect.Value ) (any , error ) {
557
+ if to .CanSet () && to .Kind () == reflect .Slice && from .Kind () == reflect .Slice {
558
+ to .Set (reflect .MakeSlice (to .Type (), from .Len (), from .Cap ()))
559
+ }
560
+
561
+ return from .Interface (), nil
562
+ }
563
+ }
564
+
537
565
type moduleFactory [T any , S any ] interface {
538
566
Create (s S ) T
539
567
}
0 commit comments