diff --git a/.chloggen/generic-configunmarshaler.yaml b/.chloggen/generic-configunmarshaler.yaml new file mode 100755 index 00000000000..fb798f7ac5d --- /dev/null +++ b/.chloggen/generic-configunmarshaler.yaml @@ -0,0 +1,16 @@ +# 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: configunmarshaler + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Consolidate package into generic implementation + +# One or more tracking issues or pull requests related to the change +issues: [6801] + +# (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: diff --git a/otelcol/configprovider.go b/otelcol/configprovider.go index fc344d83a68..411b11d6444 100644 --- a/otelcol/configprovider.go +++ b/otelcol/configprovider.go @@ -99,10 +99,10 @@ func (cm *configProvider) Get(ctx context.Context, factories Factories) (*Config } return &Config{ - Receivers: cfg.Receivers.GetReceivers(), - Processors: cfg.Processors.GetProcessors(), - Exporters: cfg.Exporters.GetExporters(), - Extensions: cfg.Extensions.GetExtensions(), + Receivers: cfg.Receivers.Configs(), + Processors: cfg.Processors.Configs(), + Exporters: cfg.Exporters.Configs(), + Extensions: cfg.Extensions.Configs(), Service: cfg.Service, }, nil } diff --git a/otelcol/internal/configunmarshaler/configs.go b/otelcol/internal/configunmarshaler/configs.go new file mode 100644 index 00000000000..75266ade8e9 --- /dev/null +++ b/otelcol/internal/configunmarshaler/configs.go @@ -0,0 +1,76 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package configunmarshaler // import "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" + +import ( + "fmt" + "reflect" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" +) + +type Configs[F component.Factory] struct { + cfgs map[component.ID]component.Config + + factories map[component.Type]F +} + +func NewConfigs[F component.Factory](factories map[component.Type]F) *Configs[F] { + return &Configs[F]{factories: factories} +} + +func (c *Configs[F]) Unmarshal(conf *confmap.Conf) error { + rawCfgs := make(map[component.ID]map[string]interface{}) + if err := conf.Unmarshal(&rawCfgs, confmap.WithErrorUnused()); err != nil { + return err + } + + // Prepare resulting map. + c.cfgs = make(map[component.ID]component.Config) + // Iterate over raw configs and create a config for each. + for id, value := range rawCfgs { + // Find factory based on component kind and type that we read from config source. + factory, ok := c.factories[id.Type()] + if !ok { + return errorUnknownType(id, reflect.ValueOf(c.factories).MapKeys()) + } + + // Create the default config for this component. + cfg := factory.CreateDefaultConfig() + + // Now that the default config struct is created we can Unmarshal into it, + // and it will apply user-defined config on top of the default. + if err := component.UnmarshalConfig(confmap.NewFromStringMap(value), cfg); err != nil { + return errorUnmarshalError(id, err) + } + + c.cfgs[id] = cfg + } + + return nil +} + +func (c *Configs[F]) Configs() map[component.ID]component.Config { + return c.cfgs +} + +func errorUnknownType(id component.ID, factories []reflect.Value) error { + return fmt.Errorf("unknown type: %q for id: %q (valid values: %v)", id.Type(), id, factories) +} + +func errorUnmarshalError(id component.ID, err error) error { + return fmt.Errorf("error reading configuration for %q: %w", id, err) +} diff --git a/otelcol/internal/configunmarshaler/configs_test.go b/otelcol/internal/configunmarshaler/configs_test.go new file mode 100644 index 00000000000..fbd14fa1018 --- /dev/null +++ b/otelcol/internal/configunmarshaler/configs_test.go @@ -0,0 +1,147 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package configunmarshaler + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/exporter/exportertest" + "go.opentelemetry.io/collector/extension/extensiontest" + "go.opentelemetry.io/collector/processor/processortest" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +var testKinds = []struct { + kind string + factories map[component.Type]component.Factory +}{ + { + kind: "receiver", + factories: map[component.Type]component.Factory{ + "nop": receivertest.NewNopFactory(), + }, + }, + { + kind: "processor", + factories: map[component.Type]component.Factory{ + "nop": processortest.NewNopFactory(), + }, + }, + { + kind: "exporter", + factories: map[component.Type]component.Factory{ + "nop": exportertest.NewNopFactory(), + }, + }, + { + kind: "extension", + factories: map[component.Type]component.Factory{ + "nop": extensiontest.NewNopFactory(), + }, + }, +} + +func TestUnmarshal(t *testing.T) { + for _, tk := range testKinds { + t.Run(tk.kind, func(t *testing.T) { + cfgs := NewConfigs(tk.factories) + conf := confmap.NewFromStringMap(map[string]interface{}{ + "nop": nil, + "nop/my" + tk.kind: nil, + }) + require.NoError(t, cfgs.Unmarshal(conf)) + + assert.Equal(t, map[component.ID]component.Config{ + component.NewID("nop"): tk.factories["nop"].CreateDefaultConfig(), + component.NewIDWithName("nop", "my"+tk.kind): tk.factories["nop"].CreateDefaultConfig(), + }, cfgs.Configs()) + }) + } +} + +func TestUnmarshalError(t *testing.T) { + for _, tk := range testKinds { + t.Run(tk.kind, func(t *testing.T) { + var testCases = []struct { + name string + conf *confmap.Conf + // string that the error must contain + expectedError string + }{ + { + name: "invalid-type", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": nil, + "/custom": nil, + }), + expectedError: "the part before / should not be empty", + }, + { + name: "invalid-name-after-slash", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": nil, + "nop/": nil, + }), + expectedError: "the part after / should not be empty", + }, + { + name: "unknown-type", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nosuch" + tk.kind: nil, + }), + expectedError: "unknown type: \"nosuch" + tk.kind + "\"", + }, + { + name: "duplicate", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop /my" + tk.kind + " ": nil, + " nop/ my" + tk.kind: nil, + }), + expectedError: "duplicate name", + }, + { + name: "invalid-section", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": map[string]interface{}{ + "unknown_section": tk.kind, + }, + }), + expectedError: "error reading configuration for \"nop\"", + }, + { + name: "invalid-sub-config", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": "tests", + }), + expectedError: "'[nop]' expected a map, got 'string'", + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + cfgs := NewConfigs(tk.factories) + err := cfgs.Unmarshal(tt.conf) + require.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + }) + } + }) + } +} diff --git a/otelcol/internal/configunmarshaler/error.go b/otelcol/internal/configunmarshaler/error.go deleted file mode 100644 index 50287ed6a77..00000000000 --- a/otelcol/internal/configunmarshaler/error.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler // import "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" - -import ( - "fmt" - "reflect" - - "go.opentelemetry.io/collector/component" -) - -func errorUnknownType(component string, id component.ID, factories []reflect.Value) error { - return fmt.Errorf("unknown %s type: %q for id: %q (valid values: %v)", component, id.Type(), id, factories) -} - -func errorUnmarshalError(component string, id component.ID, err error) error { - return fmt.Errorf("error reading %s configuration for %q: %w", component, id, err) -} diff --git a/otelcol/internal/configunmarshaler/exporters.go b/otelcol/internal/configunmarshaler/exporters.go deleted file mode 100644 index 5028450534e..00000000000 --- a/otelcol/internal/configunmarshaler/exporters.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler // import "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" - -import ( - "reflect" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/exporter" -) - -// exportersKeyName is the configuration key name for exporters section. -const exportersKeyName = "exporters" - -type Exporters struct { - exps map[component.ID]component.Config - - factories map[component.Type]exporter.Factory -} - -func NewExporters(factories map[component.Type]exporter.Factory) *Exporters { - return &Exporters{factories: factories} -} - -func (e *Exporters) Unmarshal(conf *confmap.Conf) error { - rawExps := make(map[component.ID]map[string]interface{}) - if err := conf.Unmarshal(&rawExps, confmap.WithErrorUnused()); err != nil { - return err - } - - // Prepare resulting map. - e.exps = make(map[component.ID]component.Config) - - // Iterate over Exporters and create a config for each. - for id, value := range rawExps { - // Find exporter factory based on "type" that we read from config source. - factory := e.factories[id.Type()] - if factory == nil { - return errorUnknownType(exportersKeyName, id, reflect.ValueOf(e.factories).MapKeys()) - } - - // Create the default config for this exporter. - exporterCfg := factory.CreateDefaultConfig() - - // Now that the default config struct is created we can Unmarshal into it, - // and it will apply user-defined config on top of the default. - if err := component.UnmarshalConfig(confmap.NewFromStringMap(value), exporterCfg); err != nil { - return errorUnmarshalError(exportersKeyName, id, err) - } - - e.exps[id] = exporterCfg - } - - return nil -} - -func (e *Exporters) GetExporters() map[component.ID]component.Config { - return e.exps -} diff --git a/otelcol/internal/configunmarshaler/exporters_test.go b/otelcol/internal/configunmarshaler/exporters_test.go deleted file mode 100644 index 3163a6db894..00000000000 --- a/otelcol/internal/configunmarshaler/exporters_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/exporter/exportertest" -) - -func TestExportersUnmarshal(t *testing.T) { - factories, err := exporter.MakeFactoryMap(exportertest.NewNopFactory()) - require.NoError(t, err) - - exps := NewExporters(factories) - conf := confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/myexporter": nil, - }) - require.NoError(t, exps.Unmarshal(conf)) - - cfgWithName := factories["nop"].CreateDefaultConfig() - assert.Equal(t, map[component.ID]component.Config{ - component.NewID("nop"): factories["nop"].CreateDefaultConfig(), - component.NewIDWithName("nop", "myexporter"): cfgWithName, - }, exps.GetExporters()) -} - -func TestExportersUnmarshalError(t *testing.T) { - var testCases = []struct { - name string - conf *confmap.Conf - // string that the error must contain - expectedError string - }{ - { - name: "invalid-exporter-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "/custom": nil, - }), - expectedError: "the part before / should not be empty", - }, - { - name: "invalid-exporter-name-after-slash", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/": nil, - }), - expectedError: "the part after / should not be empty", - }, - { - name: "unknown-exporter-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nosuchexporter": nil, - }), - expectedError: "unknown exporters type: \"nosuchexporter\"", - }, - { - name: "duplicate-exporter", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop /exp ": nil, - " nop/ exp": nil, - }), - expectedError: "duplicate name", - }, - { - name: "invalid-exporter-section", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": map[string]interface{}{ - "unknown_section": "exporter", - }, - }), - expectedError: "error reading exporters configuration for \"nop\"", - }, - { - name: "invalid-exporter-sub-config", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": "tests", - }), - expectedError: "'[nop]' expected a map, got 'string'", - }, - } - - factories, err := exporter.MakeFactoryMap(exportertest.NewNopFactory()) - assert.NoError(t, err) - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - exps := NewExporters(factories) - err = exps.Unmarshal(tt.conf) - require.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedError) - }) - } -} diff --git a/otelcol/internal/configunmarshaler/extensions.go b/otelcol/internal/configunmarshaler/extensions.go deleted file mode 100644 index dd3fbcf8c8f..00000000000 --- a/otelcol/internal/configunmarshaler/extensions.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler // import "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" - -import ( - "reflect" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/extension" -) - -// extensionsKeyName is the configuration key name for extensions section. -const extensionsKeyName = "extensions" - -type Extensions struct { - exts map[component.ID]component.Config - - factories map[component.Type]extension.Factory -} - -func NewExtensions(factories map[component.Type]extension.Factory) *Extensions { - return &Extensions{factories: factories} -} - -func (e *Extensions) Unmarshal(conf *confmap.Conf) error { - rawExts := make(map[component.ID]map[string]interface{}) - if err := conf.Unmarshal(&rawExts, confmap.WithErrorUnused()); err != nil { - return err - } - - // Prepare resulting map. - e.exts = make(map[component.ID]component.Config) - - // Iterate over extensions and create a config for each. - for id, value := range rawExts { - // Find extension factory based on "type" that we read from config source. - factory, ok := e.factories[id.Type()] - if !ok { - return errorUnknownType(extensionsKeyName, id, reflect.ValueOf(e.factories).MapKeys()) - } - - // Create the default config for this extension. - extensionCfg := factory.CreateDefaultConfig() - - // Now that the default config struct is created we can Unmarshal into it, - // and it will apply user-defined config on top of the default. - if err := component.UnmarshalConfig(confmap.NewFromStringMap(value), extensionCfg); err != nil { - return errorUnmarshalError(extensionsKeyName, id, err) - } - - e.exts[id] = extensionCfg - } - - return nil -} - -func (e *Extensions) GetExtensions() map[component.ID]component.Config { - return e.exts -} diff --git a/otelcol/internal/configunmarshaler/extensions_test.go b/otelcol/internal/configunmarshaler/extensions_test.go deleted file mode 100644 index 53febe9bfe3..00000000000 --- a/otelcol/internal/configunmarshaler/extensions_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/extension" - "go.opentelemetry.io/collector/extension/extensiontest" -) - -func TestExtensionsUnmarshal(t *testing.T) { - factories, err := extension.MakeFactoryMap(extensiontest.NewNopFactory()) - require.NoError(t, err) - - exts := NewExtensions(factories) - conf := confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/myextension": nil, - }) - require.NoError(t, exts.Unmarshal(conf)) - - cfgWithName := factories["nop"].CreateDefaultConfig() - assert.Equal(t, map[component.ID]component.Config{ - component.NewID("nop"): factories["nop"].CreateDefaultConfig(), - component.NewIDWithName("nop", "myextension"): cfgWithName, - }, exts.GetExtensions()) -} - -func TestExtensionsUnmarshalError(t *testing.T) { - var testCases = []struct { - name string - conf *confmap.Conf - // string that the error must contain - expectedError string - }{ - { - name: "invalid-extension-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "/custom": nil, - }), - expectedError: "the part before / should not be empty", - }, - { - name: "invalid-extension-name-after-slash", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/": nil, - }), - expectedError: "the part after / should not be empty", - }, - { - name: "unknown-extension-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nosuchextension": nil, - }), - expectedError: "unknown extensions type: \"nosuchextension\"", - }, - { - name: "duplicate-extension", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop /exp ": nil, - " nop/ exp": nil, - }), - expectedError: "duplicate name", - }, - { - name: "invalid-extension-section", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": map[string]interface{}{ - "unknown_section": "extension", - }, - }), - expectedError: "error reading extensions configuration for \"nop\"", - }, - { - name: "invalid-extension-sub-config", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": "tests", - }), - expectedError: "'[nop]' expected a map, got 'string'", - }, - } - - factories, err := extension.MakeFactoryMap(extensiontest.NewNopFactory()) - assert.NoError(t, err) - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - exts := NewExtensions(factories) - err = exts.Unmarshal(tt.conf) - require.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedError) - }) - } -} diff --git a/otelcol/internal/configunmarshaler/processors.go b/otelcol/internal/configunmarshaler/processors.go deleted file mode 100644 index 3c6e33e46ff..00000000000 --- a/otelcol/internal/configunmarshaler/processors.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler // import "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" - -import ( - "reflect" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/processor" -) - -// processorsKeyName is the configuration key name for processors section. -const processorsKeyName = "processors" - -type Processors struct { - procs map[component.ID]component.Config - - factories map[component.Type]processor.Factory -} - -func NewProcessors(factories map[component.Type]processor.Factory) *Processors { - return &Processors{factories: factories} -} - -func (p *Processors) Unmarshal(conf *confmap.Conf) error { - rawProcs := make(map[component.ID]map[string]interface{}) - if err := conf.Unmarshal(&rawProcs, confmap.WithErrorUnused()); err != nil { - return err - } - - // Prepare resulting map. - p.procs = make(map[component.ID]component.Config) - // Iterate over processors and create a config for each. - for id, value := range rawProcs { - // Find processor factory based on "type" that we read from config source. - factory := p.factories[id.Type()] - if factory == nil { - return errorUnknownType(processorsKeyName, id, reflect.ValueOf(p.factories).MapKeys()) - } - - // Create the default config for this processor. - processorCfg := factory.CreateDefaultConfig() - - // Now that the default config struct is created we can Unmarshal into it, - // and it will apply user-defined config on top of the default. - if err := component.UnmarshalConfig(confmap.NewFromStringMap(value), processorCfg); err != nil { - return errorUnmarshalError(processorsKeyName, id, err) - } - - p.procs[id] = processorCfg - } - - return nil -} - -func (p *Processors) GetProcessors() map[component.ID]component.Config { - return p.procs -} diff --git a/otelcol/internal/configunmarshaler/processors_test.go b/otelcol/internal/configunmarshaler/processors_test.go deleted file mode 100644 index 4cbbf48dafe..00000000000 --- a/otelcol/internal/configunmarshaler/processors_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/processor" - "go.opentelemetry.io/collector/processor/processortest" -) - -func TestProcessorsUnmarshal(t *testing.T) { - factories, err := processor.MakeFactoryMap(processortest.NewNopFactory()) - require.NoError(t, err) - - procs := NewProcessors(factories) - conf := confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/myprocessor": nil, - }) - require.NoError(t, procs.Unmarshal(conf)) - - cfgWithName := factories["nop"].CreateDefaultConfig() - assert.Equal(t, map[component.ID]component.Config{ - component.NewID("nop"): factories["nop"].CreateDefaultConfig(), - component.NewIDWithName("nop", "myprocessor"): cfgWithName, - }, procs.procs) -} - -func TestProcessorsUnmarshalError(t *testing.T) { - var testCases = []struct { - name string - conf *confmap.Conf - // string that the error must contain - expectedError string - }{ - { - name: "invalid-processor-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "/custom": nil, - }), - expectedError: "the part before / should not be empty", - }, - { - name: "invalid-processor-name-after-slash", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/": nil, - }), - expectedError: "the part after / should not be empty", - }, - { - name: "unknown-processor-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nosuchprocessor": nil, - }), - expectedError: "unknown processors type: \"nosuchprocessor\"", - }, - { - name: "duplicate-processor", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop /exp ": nil, - " nop/ exp": nil, - }), - expectedError: "duplicate name", - }, - { - name: "invalid-processor-section", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": map[string]interface{}{ - "unknown_section": "processor", - }, - }), - expectedError: "error reading processors configuration for \"nop\"", - }, - { - name: "invalid-processor-sub-config", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": "tests", - }), - expectedError: "'[nop]' expected a map, got 'string'", - }, - } - - factories, err := processor.MakeFactoryMap(processortest.NewNopFactory()) - assert.NoError(t, err) - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - procs := NewProcessors(factories) - err = procs.Unmarshal(tt.conf) - require.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedError) - }) - } -} diff --git a/otelcol/internal/configunmarshaler/receivers.go b/otelcol/internal/configunmarshaler/receivers.go deleted file mode 100644 index 332b0e73f46..00000000000 --- a/otelcol/internal/configunmarshaler/receivers.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler // import "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" - -import ( - "reflect" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/receiver" -) - -// receiversKeyName is the configuration key name for receivers section. -const receiversKeyName = "receivers" - -type Receivers struct { - recvs map[component.ID]component.Config - - factories map[component.Type]receiver.Factory -} - -func NewReceivers(factories map[component.Type]receiver.Factory) *Receivers { - return &Receivers{factories: factories} -} - -func (r *Receivers) Unmarshal(conf *confmap.Conf) error { - rawRecvs := make(map[component.ID]map[string]interface{}) - if err := conf.Unmarshal(&rawRecvs, confmap.WithErrorUnused()); err != nil { - return err - } - - // Prepare resulting map. - r.recvs = make(map[component.ID]component.Config) - - // Iterate over input map and create a config for each. - for id, value := range rawRecvs { - // Find receiver factory based on "type" that we read from config source. - factory := r.factories[id.Type()] - if factory == nil { - return errorUnknownType(receiversKeyName, id, reflect.ValueOf(r.factories).MapKeys()) - } - - // Create the default config for this receiver. - receiverCfg := factory.CreateDefaultConfig() - - // Now that the default config struct is created we can Unmarshal into it, - // and it will apply user-defined config on top of the default. - if err := component.UnmarshalConfig(confmap.NewFromStringMap(value), receiverCfg); err != nil { - return errorUnmarshalError(receiversKeyName, id, err) - } - - r.recvs[id] = receiverCfg - } - - return nil -} - -func (r *Receivers) GetReceivers() map[component.ID]component.Config { - return r.recvs -} diff --git a/otelcol/internal/configunmarshaler/receivers_test.go b/otelcol/internal/configunmarshaler/receivers_test.go deleted file mode 100644 index bef7a0596e7..00000000000 --- a/otelcol/internal/configunmarshaler/receivers_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configunmarshaler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/receiver/receivertest" -) - -func TestReceiversUnmarshal(t *testing.T) { - factories, err := receiver.MakeFactoryMap(receivertest.NewNopFactory()) - require.NoError(t, err) - - recvs := NewReceivers(factories) - conf := confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/myreceiver": nil, - }) - require.NoError(t, recvs.Unmarshal(conf)) - - cfgWithName := factories["nop"].CreateDefaultConfig() - assert.Equal(t, map[component.ID]component.Config{ - component.NewID("nop"): factories["nop"].CreateDefaultConfig(), - component.NewIDWithName("nop", "myreceiver"): cfgWithName, - }, recvs.GetReceivers()) -} - -func TestReceiversUnmarshalError(t *testing.T) { - var testCases = []struct { - name string - conf *confmap.Conf - // string that the error must contain - expectedError string - }{ - { - name: "invalid-receiver-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "/custom": nil, - }), - expectedError: "the part before / should not be empty", - }, - { - name: "invalid-receiver-name-after-slash", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": nil, - "nop/": nil, - }), - expectedError: "the part after / should not be empty", - }, - { - name: "unknown-receiver-type", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nosuchreceiver": nil, - }), - expectedError: "unknown receivers type: \"nosuchreceiver\"", - }, - { - name: "duplicate-receiver", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop /exp ": nil, - " nop/ exp": nil, - }), - expectedError: "duplicate name", - }, - { - name: "invalid-receiver-section", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": map[string]interface{}{ - "unknown_section": "receiver", - }, - }), - expectedError: "error reading receivers configuration for \"nop\"", - }, - { - name: "invalid-receiver-sub-config", - conf: confmap.NewFromStringMap(map[string]interface{}{ - "nop": "tests", - }), - expectedError: "'[nop]' expected a map, got 'string'", - }, - } - - factories, err := receiver.MakeFactoryMap(receivertest.NewNopFactory()) - assert.NoError(t, err) - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - recvs := NewReceivers(factories) - err = recvs.Unmarshal(tt.conf) - require.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedError) - }) - } -} diff --git a/otelcol/unmarshaler.go b/otelcol/unmarshaler.go index 43a833e95e2..482b38557fe 100644 --- a/otelcol/unmarshaler.go +++ b/otelcol/unmarshaler.go @@ -19,17 +19,21 @@ import ( "go.opentelemetry.io/collector/config/configtelemetry" "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/extension" "go.opentelemetry.io/collector/otelcol/internal/configunmarshaler" + "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/receiver" "go.opentelemetry.io/collector/service" "go.opentelemetry.io/collector/service/telemetry" ) type configSettings struct { - Receivers *configunmarshaler.Receivers `mapstructure:"receivers"` - Processors *configunmarshaler.Processors `mapstructure:"processors"` - Exporters *configunmarshaler.Exporters `mapstructure:"exporters"` - Extensions *configunmarshaler.Extensions `mapstructure:"extensions"` - Service service.ConfigService `mapstructure:"service"` + Receivers *configunmarshaler.Configs[receiver.Factory] `mapstructure:"receivers"` + Processors *configunmarshaler.Configs[processor.Factory] `mapstructure:"processors"` + Exporters *configunmarshaler.Configs[exporter.Factory] `mapstructure:"exporters"` + Extensions *configunmarshaler.Configs[extension.Factory] `mapstructure:"extensions"` + Service service.ConfigService `mapstructure:"service"` } // unmarshal the configSettings from a confmap.Conf. @@ -37,10 +41,10 @@ type configSettings struct { func unmarshal(v *confmap.Conf, factories Factories) (*configSettings, error) { // Unmarshal top level sections and validate. cfg := &configSettings{ - Receivers: configunmarshaler.NewReceivers(factories.Receivers), - Processors: configunmarshaler.NewProcessors(factories.Processors), - Exporters: configunmarshaler.NewExporters(factories.Exporters), - Extensions: configunmarshaler.NewExtensions(factories.Extensions), + Receivers: configunmarshaler.NewConfigs(factories.Receivers), + Processors: configunmarshaler.NewConfigs(factories.Processors), + Exporters: configunmarshaler.NewConfigs(factories.Exporters), + Extensions: configunmarshaler.NewConfigs(factories.Extensions), // TODO: Add a component.ServiceFactory to allow this to be defined by the Service. Service: service.ConfigService{ Telemetry: telemetry.Config{