mirror of
https://github.com/hanzoai/telemetry.git
synced 2026-10-30 20:01:49 +00:00
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Move substantial amounts of confmap's functionality to the internal package. This allows us to share the functionality with xconfmap and therefore add experimental functionality to confmap without risking breaking changes. I'm not a huge fan of the added indirection this introduces, but I think this is a fairly standard pattern, is fairly straightforward to trace from the top-level package down (e.g. confmap.Conf -> internal.Conf), and is probably the simplest way to accomplish this. <!-- Issue number if applicable --> #### Link to tracking issue Likely required for https://github.com/open-telemetry/opentelemetry-collector/issues/13421
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package internal // import "go.opentelemetry.io/collector/confmap/internal"
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/gobwas/glob"
|
|
"github.com/knadh/koanf/maps"
|
|
)
|
|
|
|
func mergeAppend(src, dest map[string]any) error {
|
|
// mergeAppend recursively merges the src map into the dest map (left to right),
|
|
// modifying and expanding the dest map in the process.
|
|
// This function does not overwrite component lists, and ensures that the
|
|
// final value is a name-aware copy of lists from src and dest.
|
|
|
|
// Compile the globs once
|
|
patterns := []string{
|
|
"service::extensions",
|
|
"service::**::receivers",
|
|
"service::**::exporters",
|
|
}
|
|
var globs []glob.Glob
|
|
for _, p := range patterns {
|
|
if g, err := glob.Compile(p); err == nil {
|
|
globs = append(globs, g)
|
|
}
|
|
}
|
|
|
|
// Flatten both source and destination maps
|
|
srcFlat, _ := maps.Flatten(src, []string{}, KeyDelimiter)
|
|
destFlat, _ := maps.Flatten(dest, []string{}, KeyDelimiter)
|
|
|
|
for sKey, sVal := range srcFlat {
|
|
if !isMatch(sKey, globs) {
|
|
continue
|
|
}
|
|
|
|
dVal, dOk := destFlat[sKey]
|
|
if !dOk {
|
|
continue // Let maps.Merge handle missing keys
|
|
}
|
|
|
|
srcVal := reflect.ValueOf(sVal)
|
|
destVal := reflect.ValueOf(dVal)
|
|
|
|
// Only merge if the value is a slice or array; let maps.Merge handle other types
|
|
if srcVal.Kind() == reflect.Slice || srcVal.Kind() == reflect.Array {
|
|
srcFlat[sKey] = mergeSlice(srcVal, destVal)
|
|
}
|
|
}
|
|
|
|
// Unflatten and merge
|
|
mergedSrc := maps.Unflatten(srcFlat, KeyDelimiter)
|
|
maps.Merge(mergedSrc, dest)
|
|
|
|
return nil
|
|
}
|
|
|
|
// isMatch checks if a key matches any glob in the list
|
|
func isMatch(key string, globs []glob.Glob) bool {
|
|
for _, g := range globs {
|
|
if g.Match(key) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func mergeSlice(src, dest reflect.Value) any {
|
|
slice := reflect.MakeSlice(src.Type(), 0, src.Cap()+dest.Cap())
|
|
for i := 0; i < dest.Len(); i++ {
|
|
slice = reflect.Append(slice, dest.Index(i))
|
|
}
|
|
|
|
for i := 0; i < src.Len(); i++ {
|
|
if isPresent(slice, src.Index(i)) {
|
|
continue
|
|
}
|
|
slice = reflect.Append(slice, src.Index(i))
|
|
}
|
|
return slice.Interface()
|
|
}
|
|
|
|
func isPresent(slice, val reflect.Value) bool {
|
|
for i := 0; i < slice.Len(); i++ {
|
|
if reflect.DeepEqual(val.Interface(), slice.Index(i).Interface()) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|