diff --git a/.chloggen/create-confmap-validate.yaml b/.chloggen/create-confmap-validate.yaml new file mode 100644 index 000000000000..4f7db5d142f3 --- /dev/null +++ b/.chloggen/create-confmap-validate.yaml @@ -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: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: confmap + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Create 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: [] diff --git a/.chloggen/deprecate-component-validate.yaml b/.chloggen/deprecate-component-validate.yaml new file mode 100644 index 000000000000..33dcedbf2d5a --- /dev/null +++ b/.chloggen/deprecate-component-validate.yaml @@ -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 `confmap`. + +# 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: [] diff --git a/component/config.go b/component/config.go index a5862798fdd8..f1517f031b8b 100644 --- a/component/config.go +++ b/component/config.go @@ -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 confmap.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.121.0] use confmap.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.121.0] use confmap.Validate. func ValidateConfig(cfg Config) error { var err error diff --git a/confmap/config.go b/confmap/config.go new file mode 100644 index 000000000000..9c8dec59fada --- /dev/null +++ b/confmap/config.go @@ -0,0 +1,206 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package confmap // import "go.opentelemetry.io/collector/confmap" + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" +) + +// Config represents an interface for a configuration struct. +// +// Implementations and/or any sub-configs (other types embedded or included in the Config implementation) +// MUST implement the 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). +type Config any + +// 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 Config) 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(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(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 +} diff --git a/confmap/config_test.go b/confmap/config_test.go new file mode 100644 index 000000000000..b830dbe65d67 --- /dev/null +++ b/confmap/config_test.go @@ -0,0 +1,321 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package confmap + +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 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::[*confmap.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::[confmap.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) + } + }) + } +} diff --git a/confmap/confmap.go b/confmap/confmap.go index aab1730687be..4481f77ec1ec 100644 --- a/confmap/confmap.go +++ b/confmap/confmap.go @@ -24,6 +24,10 @@ const ( KeyDelimiter = "::" ) +const ( + mapstructureTag = "mapstructure" +) + // New creates a new empty confmap.Conf instance. func New() *Conf { return &Conf{k: koanf.New(KeyDelimiter)} @@ -79,7 +83,7 @@ func (fn unmarshalOptionFunc) apply(set *unmarshalOption) { // Unmarshal unmarshalls the config into a struct using the given options. // Tags on the fields of the structure must be properly set. -func (l *Conf) Unmarshal(result any, opts ...UnmarshalOption) error { +func (l *Conf) Unmarshal(result Config, opts ...UnmarshalOption) error { set := unmarshalOption{} for _, opt := range opts { opt.apply(&set) @@ -94,7 +98,7 @@ type MarshalOption interface { } // Marshal encodes the config and merges it into the Conf. -func (l *Conf) Marshal(rawVal any, _ ...MarshalOption) error { +func (l *Conf) Marshal(rawVal Config, _ ...MarshalOption) error { enc := encoder.New(encoderConfig(rawVal)) data, err := enc.Encode(rawVal) if err != nil { @@ -205,7 +209,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 +411,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 diff --git a/exporter/otlpexporter/config_test.go b/exporter/otlpexporter/config_test.go index ae351535df8c..c093c42aa960 100644 --- a/exporter/otlpexporter/config_test.go +++ b/exporter/otlpexporter/config_test.go @@ -136,7 +136,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, confmap.Validate(cfg), tt.errorMsg) }) } } diff --git a/exporter/otlphttpexporter/config_test.go b/exporter/otlphttpexporter/config_test.go index e10f198960d9..542f29e1299e 100644 --- a/exporter/otlphttpexporter/config_test.go +++ b/exporter/otlphttpexporter/config_test.go @@ -12,7 +12,6 @@ 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" @@ -28,7 +27,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, confmap.Validate(cfg)) } func TestUnmarshalConfig(t *testing.T) { diff --git a/otelcol/collector.go b/otelcol/collector.go index a0b191053a15..e21b99662972 100644 --- a/otelcol/collector.go +++ b/otelcol/collector.go @@ -171,7 +171,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 = confmap.Validate(cfg); err != nil { return fmt.Errorf("invalid configuration: %w", err) } @@ -261,7 +261,7 @@ func (col *Collector) DryRun(ctx context.Context) error { return fmt.Errorf("failed to get config: %w", err) } - return component.ValidateConfig(cfg) + return confmap.Validate(cfg) } func newFallbackLogger(options []zap.Option) (*zap.Logger, error) { diff --git a/otelcol/config_test.go b/otelcol/config_test.go index e9c766fca0a7..08b95a5f2082 100644 --- a/otelcol/config_test.go +++ b/otelcol/config_test.go @@ -14,6 +14,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/confmap" "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 := confmap.Validate(cfg) if tt.expected != nil { assert.EqualError(t, err, tt.expected.Error()) } else { diff --git a/otelcol/otelcoltest/config.go b/otelcol/otelcoltest/config.go index 8e414829266e..c2d549145cd5 100644 --- a/otelcol/otelcoltest/config.go +++ b/otelcol/otelcoltest/config.go @@ -6,7 +6,6 @@ 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" @@ -41,5 +40,5 @@ func LoadConfigAndValidate(fileName string, factories otelcol.Factories) (*otelc if err != nil { return nil, err } - return cfg, component.ValidateConfig(cfg) + return cfg, confmap.Validate(cfg) } diff --git a/receiver/otlpreceiver/config_test.go b/receiver/otlpreceiver/config_test.go index dc00872dd297..b3bae8f6656d 100644 --- a/receiver/otlpreceiver/config_test.go +++ b/receiver/otlpreceiver/config_test.go @@ -193,7 +193,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, confmap.Validate(cfg), "must specify at least one protocol when using the OTLP receiver") } func TestUnmarshalConfigInvalidSignalPath(t *testing.T) { @@ -230,5 +230,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, confmap.Validate(cfg), "must specify at least one protocol when using the OTLP receiver") } diff --git a/service/config_test.go b/service/config_test.go index 8ce13a7ec338..d1ecc5f0aa03 100644 --- a/service/config_test.go +++ b/service/config_test.go @@ -13,6 +13,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/confmap" "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 := confmap.Validate(cfg) if tt.expected != nil { assert.ErrorContains(t, err, tt.expected.Error()) } else { diff --git a/service/pipelines/config_test.go b/service/pipelines/config_test.go index 19ca0be6d4a5..3a535979df87 100644 --- a/service/pipelines/config_test.go +++ b/service/pipelines/config_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" "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, confmap.Validate(cfg), tt.expected.Error()) } else { - require.NoError(t, component.ValidateConfig(cfg)) + require.NoError(t, confmap.Validate(cfg)) } // Clean up the profiles support gate, which may have been enabled in `cfgFn`.