Skip to content

Commit

Permalink
Call validate on all members
Browse files Browse the repository at this point in the history
Signed-off-by: Bogdan Drutu <[email protected]>
  • Loading branch information
bogdandrutu committed Nov 15, 2022
1 parent c266b7c commit cfca7a7
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 35 deletions.
54 changes: 54 additions & 0 deletions component/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,72 @@
package component // import "go.opentelemetry.io/collector/component"

import (
"reflect"

"go.uber.org/multierr"

"go.opentelemetry.io/collector/confmap"
)

// Type is the component type as it is used in the config.
type Type string

type Config interface {
identifiable

privateConfig()
}

// ConfigValidator defines an optional interface for configurations to implement to do validation.
type ConfigValidator interface {
// Validate the configuration and returns an error if invalid.
Validate() error
}

// ValidateConfig validates a config, by doing this:
// - Call Validate on the config itself if the config implements ConfigValidator.
// - Call Validate on every member that implements ConfigValidator. The members can be direct members of the
// config structs, part of slices/arrays or part of maps, or member of any other member.
func ValidateConfig(cfg Config) error {
return validate(reflect.ValueOf(cfg))
}

func validate(t reflect.Value) error {
// Validate the value itself.
errs := error(nil)
// If implements ConfigValidator then call Validate.
if validator, ok := t.Interface().(ConfigValidator); ok {
errs = validator.Validate()
}

if t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.Struct:
// Reflect on the pointed data and check each of its fields.
for i := 0; i < t.NumField(); i++ {
if !t.Type().Field(i).IsExported() {
continue
}
errs = multierr.Append(errs, validate(t.Field(i)))
}
case reflect.Slice, reflect.Array:
// Reflect on the pointed data and check each of its fields.
for i := 0; i < t.Len(); i++ {
errs = multierr.Append(errs, validate(t.Index(i)))
}
case reflect.Map:
iter := t.MapRange()
for iter.Next() {
errs = multierr.Append(errs, validate(iter.Key()))
errs = multierr.Append(errs, validate(iter.Value()))
}
}

return errs
}

// DataType is a special Type that represents the data types supported by the collector. We currently support
// collecting metrics, traces and logs, this can expand in the future.
type DataType = Type
Expand Down
4 changes: 1 addition & 3 deletions component/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import (
// ExporterConfig is the configuration of a component.Exporter. Specific extensions must implement
// this interface and must embed config.ExporterSettings struct or a struct that extends it.
type ExporterConfig interface {
identifiable

privateConfigExporter()
Config
}

// UnmarshalExporterConfig helper function to unmarshal an ExporterConfig.
Expand Down
4 changes: 1 addition & 3 deletions component/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ import (
// ExtensionConfig is the configuration of a component.Extension. Specific extensions must implement
// this interface and must embed config.ExtensionSettings struct or a struct that extends it.
type ExtensionConfig interface {
identifiable

privateConfigExtension()
Config
}

// UnmarshalExtensionConfig helper function to unmarshal an ExtensionConfig.
Expand Down
4 changes: 1 addition & 3 deletions component/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import (
// ProcessorConfig is the configuration of a component.Processor. Specific extensions must implement
// this interface and must embed ProcessorSettings struct or a struct that extends it.
type ProcessorConfig interface {
identifiable

privateConfigProcessor()
Config
}

// UnmarshalProcessorConfig helper function to unmarshal a ProcessorConfig.
Expand Down
4 changes: 1 addition & 3 deletions component/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import (
// ReceiverConfig is the configuration of a component.Receiver. Specific extensions must implement
// this interface and must embed ReceiverSettings struct or a struct that extends it.
type ReceiverConfig interface {
identifiable

privateConfigReceiver()
Config
}

// UnmarshalReceiverConfig helper function to unmarshal a ReceiverConfig.
Expand Down
2 changes: 1 addition & 1 deletion exporter/otlphttpexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestUnmarshalDefaultConfig(t *testing.T) {
assert.NoError(t, component.UnmarshalExporterConfig(confmap.New(), cfg))
assert.Equal(t, factory.CreateDefaultConfig(), cfg)
// Default/Empty config is invalid.
assert.Error(t, cfg.(component.ConfigValidator).Validate())
assert.Error(t, component.ValidateConfig(cfg))
}

func TestUnmarshalConfig(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions receiver/otlpreceiver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,12 @@ func TestUnmarshalConfigEmptyProtocols(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NoError(t, component.UnmarshalReceiverConfig(cm, cfg))
assert.EqualError(t, cfg.(component.ConfigValidator).Validate(), "must specify at least one protocol when using the OTLP receiver")
assert.EqualError(t, component.ValidateConfig(cfg), "must specify at least one protocol when using the OTLP receiver")
}

func TestUnmarshalConfigEmpty(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NoError(t, component.UnmarshalReceiverConfig(confmap.New(), cfg))
assert.EqualError(t, cfg.(component.ConfigValidator).Validate(), "must specify at least one protocol when using the OTLP receiver")
assert.EqualError(t, component.ValidateConfig(cfg), "must specify at least one protocol when using the OTLP receiver")
}
24 changes: 4 additions & 20 deletions service/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ func (cfg *Config) Validate() error {

// Validate the receiver configuration.
for recvID, recvCfg := range cfg.Receivers {
validator, ok := recvCfg.(component.ConfigValidator)
if !ok {
continue
}
if err := validator.Validate(); err != nil {
if err := component.ValidateConfig(recvCfg); err != nil {
return fmt.Errorf("receiver %q has invalid configuration: %w", recvID, err)
}
}
Expand All @@ -77,33 +73,21 @@ func (cfg *Config) Validate() error {

// Validate the exporter configuration.
for expID, expCfg := range cfg.Exporters {
validator, ok := expCfg.(component.ConfigValidator)
if !ok {
continue
}
if err := validator.Validate(); err != nil {
if err := component.ValidateConfig(expCfg); err != nil {
return fmt.Errorf("exporter %q has invalid configuration: %w", expID, err)
}
}

// Validate the processor configuration.
for procID, procCfg := range cfg.Processors {
validator, ok := procCfg.(component.ConfigValidator)
if !ok {
continue
}
if err := validator.Validate(); err != nil {
if err := component.ValidateConfig(procCfg); err != nil {
return fmt.Errorf("processor %q has invalid configuration: %w", procID, err)
}
}

// Validate the extension configuration.
for extID, extCfg := range cfg.Extensions {
validator, ok := extCfg.(component.ConfigValidator)
if !ok {
continue
}
if err := validator.Validate(); err != nil {
if err := component.ValidateConfig(extCfg); err != nil {
return fmt.Errorf("extension %q has invalid configuration: %w", extID, err)
}
}
Expand Down

0 comments on commit cfca7a7

Please sign in to comment.