[xconfmap] Create module and add validation facilities (#12226)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

Builds on
https://github.com/open-telemetry/opentelemetry-collector/pull/12224 and
starts the move of config validation from component to confmap. We can
keep this in `xconfmap` while we determine whether to add the ability to
validate using struct field tags.

I think this has the following advantages:
1. Everything configuration-related is now in confmap instead of split
between confmap and component.
2. We can share things like the mapstructure tag and config key
separator as constants between unmarshaling and validation without
creating dependencies between confmap and component.

~The one uncertainty this creates is what to do with `component.Config`,
which would now be used as a thin alias for `any` without any meaningful
usage in component.~

<!-- Issue number if applicable -->
#### Link to tracking issue
Fixes
https://github.com/open-telemetry/opentelemetry-collector/issues/11524
This commit is contained in:
Evan Bradley 2025-02-04 17:25:59 -05:00 committed by GitHub
parent 677b87e3ab
commit 0ac887e6b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 690 additions and 16 deletions

View File

@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: new_component
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: xconfmap
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Create the xconfmap module and add the `Validator` interface and `Validate` function to facilitate config validation
# One or more tracking issues or pull requests related to the change
issues: [11524]
# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]

View File

@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: deprecation
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: component
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Deprecate `ConfigValidator` and `ValidateConfig`
# One or more tracking issues or pull requests related to the change
issues: [11524]
# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: Please use `Validator` and `Validate` respectively from `xconfmap`.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]

View File

@ -53,6 +53,7 @@ var replaceModules = []string{
"/config/configtelemetry",
"/config/configtls",
"/confmap",
"/confmap/xconfmap",
"/confmap/provider/envprovider",
"/confmap/provider/fileprovider",
"/confmap/provider/httpprovider",

View File

@ -52,6 +52,7 @@ replaces:
- go.opentelemetry.io/collector/config/configtelemetry => ../../config/configtelemetry
- go.opentelemetry.io/collector/config/configtls => ../../config/configtls
- go.opentelemetry.io/collector/confmap => ../../confmap
- go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
- go.opentelemetry.io/collector/confmap/provider/envprovider => ../../confmap/provider/envprovider
- go.opentelemetry.io/collector/confmap/provider/fileprovider => ../../confmap/provider/fileprovider
- go.opentelemetry.io/collector/confmap/provider/httpprovider => ../../confmap/provider/httpprovider

View File

@ -93,6 +93,7 @@ require (
go.opentelemetry.io/collector/config/configretry v1.25.0 // indirect
go.opentelemetry.io/collector/config/configtelemetry v0.119.0 // indirect
go.opentelemetry.io/collector/config/configtls v1.25.0 // indirect
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000 // indirect
go.opentelemetry.io/collector/connector/connectortest v0.119.0 // indirect
go.opentelemetry.io/collector/connector/xconnector v0.119.0 // indirect
go.opentelemetry.io/collector/consumer v1.25.0 // indirect
@ -190,6 +191,8 @@ replace go.opentelemetry.io/collector/config/configtls => ../../config/configtls
replace go.opentelemetry.io/collector/confmap => ../../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
replace go.opentelemetry.io/collector/confmap/provider/envprovider => ../../confmap/provider/envprovider
replace go.opentelemetry.io/collector/confmap/provider/fileprovider => ../../confmap/provider/fileprovider

View File

@ -14,7 +14,7 @@ import (
// Config defines the configuration for a component.Component.
//
// Implementations and/or any sub-configs (other types embedded or included in the Config implementation)
// MUST implement the ConfigValidator if any validation is required for that part of the configuration
// MUST implement xconfmap.Validator if any validation is required for that part of the configuration
// (e.g. check if a required field is present).
//
// A valid implementation MUST pass the check componenttest.CheckConfigStruct (return nil error).
@ -25,6 +25,8 @@ type Config any
var configValidatorType = reflect.TypeOf((*ConfigValidator)(nil)).Elem()
// ConfigValidator defines an optional interface for configurations to implement to do validation.
//
// Deprecated: [v0.120.0] use xconfmap.Validator.
type ConfigValidator interface {
// Validate the configuration and returns an error if invalid.
Validate() error
@ -32,6 +34,8 @@ type ConfigValidator interface {
// ValidateConfig validates a config, by doing this:
// - Call Validate on the config itself if the config implements ConfigValidator.
//
// Deprecated: [v0.120.0] use xconfmap.Validate.
func ValidateConfig(cfg Config) error {
var err error

View File

@ -24,6 +24,12 @@ const (
KeyDelimiter = "::"
)
const (
// MapstructureTag is the struct field tag used to record marshaling/unmarshaling settings.
// See https://pkg.go.dev/github.com/go-viper/mapstructure/v2 for supported values.
MapstructureTag = "mapstructure"
)
// New creates a new empty confmap.Conf instance.
func New() *Conf {
return &Conf{k: koanf.New(KeyDelimiter)}
@ -205,7 +211,7 @@ func decodeConfig(m *Conf, result any, errorUnused bool, skipTopLevelUnmarshaler
dc := &mapstructure.DecoderConfig{
ErrorUnused: errorUnused,
Result: result,
TagName: "mapstructure",
TagName: MapstructureTag,
WeaklyTypedInput: false,
MatchName: caseSensitiveMatchName,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
@ -407,7 +413,7 @@ func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue {
for i := 0; i < to.Type().NumField(); i++ {
// embedded structs passed in via `squash` cannot be pointers. We just check if they are structs:
f := to.Type().Field(i)
if f.IsExported() && slices.Contains(strings.Split(f.Tag.Get("mapstructure"), ","), "squash") {
if f.IsExported() && slices.Contains(strings.Split(f.Tag.Get(MapstructureTag), ","), "squash") {
if unmarshaler, ok := to.Field(i).Addr().Interface().(Unmarshaler); ok {
c := NewFromStringMap(fromAsMap)
c.skipTopLevelUnmarshaler = true

View File

@ -0,0 +1 @@
include ../../Makefile.Common

199
confmap/xconfmap/config.go Normal file
View File

@ -0,0 +1,199 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package xconfmap // import "go.opentelemetry.io/collector/confmap/xconfmap"
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"go.opentelemetry.io/collector/confmap"
)
// As interface types are only used for static typing, a common idiom to find the reflection Type
// for an interface type Foo is to use a *Foo value.
var configValidatorType = reflect.TypeOf((*Validator)(nil)).Elem()
// Validator defines an optional interface for configurations to implement to do validation.
type Validator interface {
// Validate the configuration and returns an error if invalid.
Validate() error
}
// Validate validates a config, by doing this:
// - Call Validate on the config itself if the config implements ConfigValidator.
func Validate(cfg any) error {
var err error
for _, validationErr := range validate(reflect.ValueOf(cfg)) {
err = errors.Join(err, validationErr)
}
return err
}
type pathError struct {
err error
path []string
}
func (pe pathError) Error() string {
if len(pe.path) > 0 {
var path string
sb := strings.Builder{}
_, _ = sb.WriteString(pe.path[len(pe.path)-1])
for i := len(pe.path) - 2; i >= 0; i-- {
_, _ = sb.WriteString(confmap.KeyDelimiter)
_, _ = sb.WriteString(pe.path[i])
}
path = sb.String()
return fmt.Sprintf("%s: %s", path, pe.err)
}
return pe.err.Error()
}
func (pe pathError) Unwrap() error {
return pe.err
}
func validate(v reflect.Value) []pathError {
errs := []pathError{}
// Validate the value itself.
switch v.Kind() {
case reflect.Invalid:
return nil
case reflect.Ptr, reflect.Interface:
return validate(v.Elem())
case reflect.Struct:
err := callValidateIfPossible(v)
if err != nil {
errs = append(errs, pathError{err: err})
}
// Reflect on the pointed data and check each of its fields.
for i := 0; i < v.NumField(); i++ {
if !v.Type().Field(i).IsExported() {
continue
}
field := v.Type().Field(i)
path := fieldName(field)
subpathErrs := validate(v.Field(i))
for _, err := range subpathErrs {
errs = append(errs, pathError{
err: err.err,
path: append(err.path, path),
})
}
}
return errs
case reflect.Slice, reflect.Array:
err := callValidateIfPossible(v)
if err != nil {
errs = append(errs, pathError{err: err})
}
// Reflect on the pointed data and check each of its fields.
for i := 0; i < v.Len(); i++ {
subPathErrs := validate(v.Index(i))
for _, err := range subPathErrs {
errs = append(errs, pathError{
err: err.err,
path: append(err.path, strconv.Itoa(i)),
})
}
}
return errs
case reflect.Map:
err := callValidateIfPossible(v)
if err != nil {
errs = append(errs, pathError{err: err})
}
iter := v.MapRange()
for iter.Next() {
keyErrs := validate(iter.Key())
valueErrs := validate(iter.Value())
key := stringifyMapKey(iter.Key())
for _, err := range keyErrs {
errs = append(errs, pathError{err: err.err, path: append(err.path, key)})
}
for _, err := range valueErrs {
errs = append(errs, pathError{err: err.err, path: append(err.path, key)})
}
}
return errs
default:
err := callValidateIfPossible(v)
if err != nil {
return []pathError{{err: err}}
}
return nil
}
}
func callValidateIfPossible(v reflect.Value) error {
// If the value type implements ConfigValidator just call Validate
if v.Type().Implements(configValidatorType) {
return v.Interface().(Validator).Validate()
}
// If the pointer type implements ConfigValidator call Validate on the pointer to the current value.
if reflect.PointerTo(v.Type()).Implements(configValidatorType) {
// If not addressable, then create a new *V pointer and set the value to current v.
if !v.CanAddr() {
pv := reflect.New(reflect.PointerTo(v.Type()).Elem())
pv.Elem().Set(v)
v = pv.Elem()
}
return v.Addr().Interface().(Validator).Validate()
}
return nil
}
func fieldName(field reflect.StructField) string {
var fieldName string
if tag, ok := field.Tag.Lookup(confmap.MapstructureTag); ok {
tags := strings.Split(tag, ",")
if len(tags) > 0 {
fieldName = tags[0]
}
}
// Even if the mapstructure tag exists, the field name may not
// be available, so set it if it is still blank.
if len(fieldName) == 0 {
fieldName = strings.ToLower(field.Name)
}
return fieldName
}
func stringifyMapKey(val reflect.Value) string {
var key string
if str, ok := val.Interface().(string); ok {
key = str
} else if stringer, ok := val.Interface().(fmt.Stringer); ok {
key = stringer.String()
} else {
switch val.Kind() {
case reflect.Ptr, reflect.Interface, reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
key = fmt.Sprintf("[%T key]", val.Interface())
default:
key = fmt.Sprintf("%v", val.Interface())
}
}
return key
}

View File

@ -0,0 +1,323 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package xconfmap
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
type configChildStruct struct {
Child errValidateConfig
ChildPtr *errValidateConfig
}
type configChildSlice struct {
Child []errValidateConfig
ChildPtr []*errValidateConfig
}
type configChildMapValue struct {
Child map[string]errValidateConfig
ChildPtr map[string]*errValidateConfig
}
type configChildMapKey struct {
Child map[errType]string
ChildPtr map[*errType]string
}
type configChildTypeDef struct {
Child errType
ChildPtr *errType
}
type config any
type configChildInterface struct {
Child config
}
type errValidateConfig struct {
err error
}
func (e *errValidateConfig) Validate() error {
return e.err
}
type errType string
func (e errType) Validate() error {
if e == "" {
return nil
}
return errors.New(string(e))
}
func newErrType(etStr string) *errType {
et := errType(etStr)
return &et
}
type errMapType map[string]string
func (e errMapType) Validate() error {
return errors.New(e["err"])
}
type structKey struct {
k string
e error
}
func (s structKey) String() string {
return s.k
}
func (s structKey) Validate() error {
return s.e
}
type configChildMapCustomKey struct {
Child map[structKey]errValidateConfig
}
func newErrMapType() *errMapType {
et := errMapType(nil)
return &et
}
type configMapstructure struct {
Valid *errValidateConfig `mapstructure:"validtag,omitempty"`
NoData *errValidateConfig `mapstructure:""`
NoName *errValidateConfig `mapstructure:",remain"`
}
type configDeeplyNested struct {
MapKeyChild map[configChildStruct]string
MapValueChild map[string]configChildStruct
SliceChild []configChildSlice
MapIntKey map[int]errValidateConfig
MapFloatKey map[float64]errValidateConfig
}
type sliceTypeAlias []configChildSlice
func (sliceTypeAlias) Validate() error {
return errors.New("sliceTypeAlias error")
}
func TestValidateConfig(t *testing.T) {
tests := []struct {
name string
cfg any
expected error
}{
{
name: "struct",
cfg: errValidateConfig{err: errors.New("struct")},
expected: errors.New("struct"),
},
{
name: "pointer struct",
cfg: &errValidateConfig{err: errors.New("pointer struct")},
expected: errors.New("pointer struct"),
},
{
name: "type",
cfg: errType("type"),
expected: errors.New("type"),
},
{
name: "pointer child",
cfg: newErrType("pointer type"),
expected: errors.New("pointer type"),
},
{
name: "child interface with nil",
cfg: configChildInterface{},
expected: nil,
},
{
name: "pointer to child interface with nil",
cfg: &configChildInterface{},
expected: nil,
},
{
name: "nil",
cfg: nil,
expected: nil,
},
{
name: "nil map type",
cfg: errMapType(nil),
expected: errors.New(""),
},
{
name: "nil pointer map type",
cfg: newErrMapType(),
expected: errors.New(""),
},
{
name: "child struct",
cfg: configChildStruct{Child: errValidateConfig{err: errors.New("child struct")}},
expected: errors.New("child: child struct"),
},
{
name: "pointer child struct",
cfg: &configChildStruct{Child: errValidateConfig{err: errors.New("pointer child struct")}},
expected: errors.New("child: pointer child struct"),
},
{
name: "child struct pointer",
cfg: &configChildStruct{ChildPtr: &errValidateConfig{err: errors.New("child struct pointer")}},
expected: errors.New("childptr: child struct pointer"),
},
{
name: "child interface",
cfg: configChildInterface{Child: errValidateConfig{err: errors.New("child interface")}},
expected: errors.New("child: child interface"),
},
{
name: "pointer to child interface",
cfg: &configChildInterface{Child: errValidateConfig{err: errors.New("pointer to child interface")}},
expected: errors.New("child: pointer to child interface"),
},
{
name: "child interface with pointer",
cfg: configChildInterface{Child: &errValidateConfig{err: errors.New("child interface with pointer")}},
expected: errors.New("child: child interface with pointer"),
},
{
name: "pointer to child interface with pointer",
cfg: &configChildInterface{Child: &errValidateConfig{err: errors.New("pointer to child interface with pointer")}},
expected: errors.New("child: pointer to child interface with pointer"),
},
{
name: "child slice",
cfg: configChildSlice{Child: []errValidateConfig{{}, {err: errors.New("child slice")}}},
expected: errors.New("child::1: child slice"),
},
{
name: "pointer child slice",
cfg: &configChildSlice{Child: []errValidateConfig{{}, {err: errors.New("pointer child slice")}}},
expected: errors.New("child::1: pointer child slice"),
},
{
name: "child slice pointer",
cfg: &configChildSlice{ChildPtr: []*errValidateConfig{{}, {err: errors.New("child slice pointer")}}},
expected: errors.New("childptr::1: child slice pointer"),
},
{
name: "child map value",
cfg: configChildMapValue{Child: map[string]errValidateConfig{"test": {err: errors.New("child map")}}},
expected: errors.New("child::test: child map"),
},
{
name: "pointer child map value",
cfg: &configChildMapValue{Child: map[string]errValidateConfig{"test": {err: errors.New("pointer child map")}}},
expected: errors.New("child::test: pointer child map"),
},
{
name: "child map value pointer",
cfg: &configChildMapValue{ChildPtr: map[string]*errValidateConfig{"test": {err: errors.New("child map pointer")}}},
expected: errors.New("childptr::test: child map pointer"),
},
{
name: "child map key",
cfg: configChildMapKey{Child: map[errType]string{"child_map_key": ""}},
expected: errors.New("child::child_map_key: child_map_key"),
},
{
name: "pointer child map key",
cfg: &configChildMapKey{Child: map[errType]string{"pointer_child_map_key": ""}},
expected: errors.New("child::pointer_child_map_key: pointer_child_map_key"),
},
{
name: "child map key pointer",
cfg: &configChildMapKey{ChildPtr: map[*errType]string{newErrType("child map key pointer"): ""}},
expected: errors.New("childptr::[*xconfmap.errType key]: child map key pointer"),
},
{
name: "map with stringified non-string key type",
cfg: &configChildMapCustomKey{Child: map[structKey]errValidateConfig{{k: "struct_key", e: errors.New("custom key error")}: {err: errors.New("value error")}}},
expected: errors.New("child::struct_key: custom key error\nchild::struct_key: value error"),
},
{
name: "child type",
cfg: configChildTypeDef{Child: "child type"},
expected: errors.New("child: child type"),
},
{
name: "pointer child type",
cfg: &configChildTypeDef{Child: "pointer child type"},
expected: errors.New("child: pointer child type"),
},
{
name: "child type pointer",
cfg: &configChildTypeDef{ChildPtr: newErrType("child type pointer")},
expected: errors.New("childptr: child type pointer"),
},
{
name: "valid mapstructure tag",
cfg: configMapstructure{Valid: &errValidateConfig{errors.New("test")}},
expected: errors.New("validtag: test"),
},
{
name: "zero-length mapstructure tag",
cfg: configMapstructure{NoData: &errValidateConfig{errors.New("test")}},
expected: errors.New("nodata: test"),
},
{
name: "no field name in mapstructure tag",
cfg: configMapstructure{NoName: &errValidateConfig{errors.New("test")}},
expected: errors.New("noname: test"),
},
{
name: "nested map key error",
cfg: configDeeplyNested{MapKeyChild: map[configChildStruct]string{{Child: errValidateConfig{err: errors.New("child key error")}}: "val"}},
expected: errors.New("mapkeychild::[xconfmap.configChildStruct key]::child: child key error"),
},
{
name: "nested map value error",
cfg: configDeeplyNested{MapValueChild: map[string]configChildStruct{"key": {Child: errValidateConfig{err: errors.New("child key error")}}}},
expected: errors.New("mapvaluechild::key::child: child key error"),
},
{
name: "nested slice value error",
cfg: configDeeplyNested{SliceChild: []configChildSlice{{Child: []errValidateConfig{{err: errors.New("child key error")}}}}},
expected: errors.New("slicechild::0::child::0: child key error"),
},
{
name: "nested map with int key",
cfg: configDeeplyNested{MapIntKey: map[int]errValidateConfig{1: {err: errors.New("int key error")}}},
expected: errors.New("mapintkey::1: int key error"),
},
{
name: "nested map with float key",
cfg: configDeeplyNested{MapFloatKey: map[float64]errValidateConfig{1.2: {err: errors.New("float key error")}}},
expected: errors.New("mapfloatkey::1.2: float key error"),
},
{
name: "slice type alias",
cfg: sliceTypeAlias{},
expected: errors.New("sliceTypeAlias error"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := Validate(tt.cfg)
if tt.expected != nil {
assert.EqualError(t, err, tt.expected.Error())
} else {
assert.NoError(t, err)
}
})
}
}

24
confmap/xconfmap/go.mod Normal file
View File

@ -0,0 +1,24 @@
module go.opentelemetry.io/collector/confmap/xconfmap
go 1.22.0
require (
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/collector/confmap v1.25.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/knadh/koanf/providers/confmap v0.1.0 // indirect
github.com/knadh/koanf/v2 v2.1.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace go.opentelemetry.io/collector/confmap => ../

35
confmap/xconfmap/go.sum generated Normal file
View File

@ -0,0 +1,35 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU=
github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU=
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -19,6 +19,7 @@ import (
"go.opentelemetry.io/collector/config/configtls"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/exporter/exporterbatcher"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)
@ -136,7 +137,7 @@ func TestUnmarshalInvalidConfig(t *testing.T) {
sub, err := cm.Sub(tt.name)
require.NoError(t, err)
assert.NoError(t, sub.Unmarshal(&cfg))
assert.ErrorContains(t, component.ValidateConfig(cfg), tt.errorMsg)
assert.ErrorContains(t, xconfmap.Validate(cfg), tt.errorMsg)
})
}
}

View File

@ -14,6 +14,7 @@ require (
go.opentelemetry.io/collector/config/configretry v1.25.0
go.opentelemetry.io/collector/config/configtls v1.25.0
go.opentelemetry.io/collector/confmap v1.25.0
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/consumer v1.25.0
go.opentelemetry.io/collector/consumer/consumererror v0.119.0
go.opentelemetry.io/collector/exporter v0.119.0
@ -98,6 +99,8 @@ replace go.opentelemetry.io/collector/config/configtls => ../../config/configtls
replace go.opentelemetry.io/collector/confmap => ../../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
replace go.opentelemetry.io/collector/exporter => ../
replace go.opentelemetry.io/collector/extension => ../../extension

View File

@ -12,13 +12,13 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/config/configopaque"
"go.opentelemetry.io/collector/config/configretry"
"go.opentelemetry.io/collector/config/configtls"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)
@ -28,7 +28,7 @@ func TestUnmarshalDefaultConfig(t *testing.T) {
require.NoError(t, confmap.New().Unmarshal(&cfg))
assert.Equal(t, factory.CreateDefaultConfig(), cfg)
// Default/Empty config is invalid.
assert.Error(t, component.ValidateConfig(cfg))
assert.Error(t, xconfmap.Validate(cfg))
}
func TestUnmarshalConfig(t *testing.T) {

View File

@ -13,6 +13,7 @@ require (
go.opentelemetry.io/collector/config/configretry v1.25.0
go.opentelemetry.io/collector/config/configtls v1.25.0
go.opentelemetry.io/collector/confmap v1.25.0
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/consumer v1.25.0
go.opentelemetry.io/collector/consumer/consumererror v0.119.0
go.opentelemetry.io/collector/exporter v0.119.0
@ -98,6 +99,8 @@ replace go.opentelemetry.io/collector/config/configtls => ../../config/configtls
replace go.opentelemetry.io/collector/confmap => ../../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
replace go.opentelemetry.io/collector/exporter => ../
replace go.opentelemetry.io/collector/extension => ../../extension

View File

@ -177,6 +177,8 @@ replace go.opentelemetry.io/collector/extension => ../../extension
replace go.opentelemetry.io/collector/confmap => ../../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
replace go.opentelemetry.io/collector/component => ../../component
replace go.opentelemetry.io/collector/component/componenttest => ../../component/componenttest

View File

@ -21,6 +21,7 @@ import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/extension"
"go.opentelemetry.io/collector/otelcol/internal/grpclog"
"go.opentelemetry.io/collector/service"
@ -171,7 +172,7 @@ func (col *Collector) setupConfigurationComponents(ctx context.Context) error {
return fmt.Errorf("failed to get config: %w", err)
}
if err = component.ValidateConfig(cfg); err != nil {
if err = xconfmap.Validate(cfg); err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}
@ -261,7 +262,7 @@ func (col *Collector) DryRun(ctx context.Context) error {
return fmt.Errorf("failed to get config: %w", err)
}
return component.ValidateConfig(cfg)
return xconfmap.Validate(cfg)
}
func newFallbackLogger(options []zap.Option) (*zap.Logger, error) {

View File

@ -14,6 +14,7 @@ import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/pipeline"
"go.opentelemetry.io/collector/service"
"go.opentelemetry.io/collector/service/pipelines"
@ -241,7 +242,7 @@ func TestConfigValidate(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
cfg := tt.cfgFn()
err := component.ValidateConfig(cfg)
err := xconfmap.Validate(cfg)
if tt.expected != nil {
assert.EqualError(t, err, tt.expected.Error())
} else {

View File

@ -9,6 +9,7 @@ require (
go.opentelemetry.io/collector/component/componentstatus v0.119.0
go.opentelemetry.io/collector/config/configtelemetry v0.119.0
go.opentelemetry.io/collector/confmap v1.25.0
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/connector v0.119.0
go.opentelemetry.io/collector/connector/connectortest v0.119.0
go.opentelemetry.io/collector/exporter v0.119.0
@ -141,6 +142,8 @@ replace go.opentelemetry.io/collector/exporter => ../exporter
replace go.opentelemetry.io/collector/confmap => ../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../confmap/xconfmap
replace go.opentelemetry.io/collector/config/configtelemetry => ../config/configtelemetry
replace go.opentelemetry.io/collector/processor => ../processor

View File

@ -6,12 +6,12 @@ package otelcoltest // import "go.opentelemetry.io/collector/otelcol/otelcoltest
import (
"context"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/provider/envprovider"
"go.opentelemetry.io/collector/confmap/provider/fileprovider"
"go.opentelemetry.io/collector/confmap/provider/httpprovider"
"go.opentelemetry.io/collector/confmap/provider/yamlprovider"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/otelcol"
)
@ -41,5 +41,5 @@ func LoadConfigAndValidate(fileName string, factories otelcol.Factories) (*otelc
if err != nil {
return nil, err
}
return cfg, component.ValidateConfig(cfg)
return cfg, xconfmap.Validate(cfg)
}

View File

@ -10,6 +10,7 @@ require (
go.opentelemetry.io/collector/confmap/provider/fileprovider v1.25.0
go.opentelemetry.io/collector/confmap/provider/httpprovider v1.25.0
go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.25.0
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/connector v0.119.0
go.opentelemetry.io/collector/connector/connectortest v0.119.0
go.opentelemetry.io/collector/exporter v0.119.0
@ -216,6 +217,8 @@ replace go.opentelemetry.io/collector/extension/xextension => ../../extension/xe
replace go.opentelemetry.io/collector/confmap => ../../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
replace go.opentelemetry.io/collector/processor/processortest => ../../processor/processortest
replace go.opentelemetry.io/collector/pdata/testdata => ../../pdata/testdata

View File

@ -19,6 +19,7 @@ import (
"go.opentelemetry.io/collector/config/configtls"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/confmap/xconfmap"
)
func TestUnmarshalDefaultConfig(t *testing.T) {
@ -193,7 +194,7 @@ func TestUnmarshalConfigEmptyProtocols(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
require.NoError(t, cm.Unmarshal(&cfg))
assert.EqualError(t, component.ValidateConfig(cfg), "must specify at least one protocol when using the OTLP receiver")
assert.EqualError(t, xconfmap.Validate(cfg), "must specify at least one protocol when using the OTLP receiver")
}
func TestUnmarshalConfigInvalidSignalPath(t *testing.T) {
@ -230,5 +231,5 @@ func TestUnmarshalConfigEmpty(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
require.NoError(t, confmap.New().Unmarshal(&cfg))
assert.EqualError(t, component.ValidateConfig(cfg), "must specify at least one protocol when using the OTLP receiver")
assert.EqualError(t, xconfmap.Validate(cfg), "must specify at least one protocol when using the OTLP receiver")
}

View File

@ -16,6 +16,7 @@ require (
go.opentelemetry.io/collector/config/confignet v1.25.0
go.opentelemetry.io/collector/config/configtls v1.25.0
go.opentelemetry.io/collector/confmap v1.25.0
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/consumer v1.25.0
go.opentelemetry.io/collector/consumer/consumererror v0.119.0
go.opentelemetry.io/collector/consumer/consumertest v0.119.0
@ -98,6 +99,8 @@ replace go.opentelemetry.io/collector/config/configtls => ../../config/configtls
replace go.opentelemetry.io/collector/confmap => ../../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../../confmap/xconfmap
replace go.opentelemetry.io/collector/extension => ../../extension
replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth

View File

@ -13,6 +13,7 @@ import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/pipeline"
"go.opentelemetry.io/collector/service/extensions"
"go.opentelemetry.io/collector/service/pipelines"
@ -77,7 +78,7 @@ func TestConfigValidate(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
cfg := tt.cfgFn()
err := component.ValidateConfig(cfg)
err := xconfmap.Validate(cfg)
if tt.expected != nil {
assert.ErrorContains(t, err, tt.expected.Error())
} else {

View File

@ -15,6 +15,7 @@ require (
go.opentelemetry.io/collector/config/confighttp v0.119.0
go.opentelemetry.io/collector/config/configtelemetry v0.119.0
go.opentelemetry.io/collector/confmap v1.25.0
go.opentelemetry.io/collector/confmap/xconfmap v0.0.0-00010101000000-000000000000
go.opentelemetry.io/collector/connector v0.119.0
go.opentelemetry.io/collector/connector/connectortest v0.119.0
go.opentelemetry.io/collector/connector/xconnector v0.119.0
@ -150,6 +151,8 @@ replace go.opentelemetry.io/collector/exporter => ../exporter
replace go.opentelemetry.io/collector/confmap => ../confmap
replace go.opentelemetry.io/collector/confmap/xconfmap => ../confmap/xconfmap
replace go.opentelemetry.io/collector/config/configtelemetry => ../config/configtelemetry
replace go.opentelemetry.io/collector/pipeline => ../pipeline

View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap/xconfmap"
"go.opentelemetry.io/collector/featuregate"
"go.opentelemetry.io/collector/pipeline"
"go.opentelemetry.io/collector/pipeline/xpipeline"
@ -108,9 +109,9 @@ func TestConfigValidate(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
cfg := tt.cfgFn(t)
if tt.expected != nil {
require.ErrorContains(t, component.ValidateConfig(cfg), tt.expected.Error())
require.ErrorContains(t, xconfmap.Validate(cfg), tt.expected.Error())
} else {
require.NoError(t, component.ValidateConfig(cfg))
require.NoError(t, xconfmap.Validate(cfg))
}
// Clean up the profiles support gate, which may have been enabled in `cfgFn`.

View File

@ -32,6 +32,7 @@ module-sets:
- go.opentelemetry.io/collector/component
- go.opentelemetry.io/collector/component/componenttest
- go.opentelemetry.io/collector/component/componentstatus
- go.opentelemetry.io/collector/confmap/xconfmap
- go.opentelemetry.io/collector/config/configauth
- go.opentelemetry.io/collector/config/configgrpc
- go.opentelemetry.io/collector/config/confighttp