Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump github.com/tcnksm/ghr from 0.13.0 to 0.16.0 in /internal/tools #118

60 changes: 60 additions & 0 deletions experimental/configsource/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 configsource

import (
"context"
"fmt"

"go.uber.org/zap"
)

// Private error types to help with testability.
type (
errConfigSourceCreation struct{ error }
errFactoryCreatedNil struct{ error }
)

// Build builds the ConfigSource objects according to the given ConfigSettings.
func Build(ctx context.Context, configSourcesSettings map[string]ConfigSettings, params CreateParams, factories Factories) (map[string]ConfigSource, error) {
cfgSources := make(map[string]ConfigSource, len(configSourcesSettings))
for fullName, cfgSrcSettings := range configSourcesSettings {
// If we have the setting we also have the factory.
factory, ok := factories[cfgSrcSettings.Type()]
if !ok {
return nil, &errUnknownType{
fmt.Errorf("unknown %s config source type for %s", cfgSrcSettings.Type(), fullName),
}
}

params.Logger = params.Logger.With(zap.String("config_source", fullName))
cfgSrc, err := factory.CreateConfigSource(ctx, params, cfgSrcSettings)
if err != nil {
return nil, &errConfigSourceCreation{
fmt.Errorf("failed to create config source %s: %w", fullName, err),
}
}

if cfgSrc == nil {
return nil, &errFactoryCreatedNil{
fmt.Errorf("factory for %q produced a nil extension", fullName),
}
}

cfgSources[fullName] = cfgSrc
}

return cfgSources, nil
}
152 changes: 152 additions & 0 deletions experimental/configsource/builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// 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 configsource

import (
"context"
"errors"
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config"
)

func TestConfigSourceBuild(t *testing.T) {
ctx := context.Background()
params := CreateParams{
Logger: zap.NewNop(),
ApplicationStartInfo: component.ApplicationStartInfo{},
}

testFactories := Factories{
"tstcfgsrc": &mockCfgSrcFactory{},
}

tests := []struct {
name string
configSettings map[string]ConfigSettings
factories Factories
expectedCfgSources map[string]ConfigSource
wantErr error
}{
{
name: "unknown_config_source",
configSettings: map[string]ConfigSettings{
"tstcfgsrc": &mockCfgSrcSettings{
Settings: Settings{
TypeVal: "unknown_config_source",
NameVal: "tstcfgsrc",
},
},
},
factories: testFactories,
wantErr: &errUnknownType{},
},
{
name: "creation_error",
configSettings: map[string]ConfigSettings{
"tstcfgsrc": &mockCfgSrcSettings{
Settings: Settings{
TypeVal: "tstcfgsrc",
NameVal: "tstcfgsrc",
},
},
},
factories: Factories{
"tstcfgsrc": &mockCfgSrcFactory{
ErrOnCreateConfigSource: errors.New("forced test error"),
},
},
wantErr: &errConfigSourceCreation{},
},
{
name: "factory_return_nil",
configSettings: map[string]ConfigSettings{
"tstcfgsrc": &mockCfgSrcSettings{
Settings: Settings{
TypeVal: "tstcfgsrc",
NameVal: "tstcfgsrc",
},
},
},
factories: Factories{
"tstcfgsrc": &mockNilCfgSrcFactory{},
},
wantErr: &errFactoryCreatedNil{},
},
{
name: "base_case",
configSettings: map[string]ConfigSettings{
"tstcfgsrc/named": &mockCfgSrcSettings{
Settings: Settings{
TypeVal: "tstcfgsrc",
NameVal: "tstcfgsrc/named",
},
Endpoint: "some_endpoint",
Token: "some_token",
},
},
factories: testFactories,
expectedCfgSources: map[string]ConfigSource{
"tstcfgsrc/named": &testConfigSource{
ValueMap: map[string]valueEntry{
"tstcfgsrc/named": {
Value: &mockCfgSrcSettings{
Settings: Settings{
TypeVal: "tstcfgsrc",
NameVal: "tstcfgsrc/named",
},
Endpoint: "some_endpoint",
Token: "some_token",
},
},
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
builtCfgSources, err := Build(ctx, tt.configSettings, params, tt.factories)
require.IsType(t, tt.wantErr, err)
require.Equal(t, tt.expectedCfgSources, builtCfgSources)
})
}
}

type mockNilCfgSrcFactory struct{}

func (m *mockNilCfgSrcFactory) Type() config.Type {
return "tstcfgsrc"
}

var _ (Factory) = (*mockNilCfgSrcFactory)(nil)

func (m *mockNilCfgSrcFactory) CreateDefaultConfig() ConfigSettings {
return &mockCfgSrcSettings{
Settings: Settings{
TypeVal: "tstcfgsrc",
},
Endpoint: "default_endpoint",
}
}

func (m *mockNilCfgSrcFactory) CreateConfigSource(context.Context, CreateParams, ConfigSettings) (ConfigSource, error) {
return nil, nil
}
54 changes: 54 additions & 0 deletions experimental/configsource/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 configsource

import (
"context"

"go.uber.org/zap"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config"
)

// CreateParams is passed to Factory.Create* functions.
type CreateParams struct {
// Logger that the factory can use during creation and can pass to the created
// ConfigSource to be used later as well.
Logger *zap.Logger

// ApplicationStartInfo can be used to retrieve data according to version, etc.
ApplicationStartInfo component.ApplicationStartInfo
}

// Factory is a factory interface for configuration sources.
type Factory interface {
component.Factory

// CreateDefaultConfig creates the default configuration settings for the ConfigSource.
// This method can be called multiple times depending on the pipeline
// configuration and should not cause side-effects that prevent the creation
// of multiple instances of the ConfigSource.
// The object returned by this method needs to pass the checks implemented by
// 'configcheck.ValidateConfig'. It is recommended to have such check in the
// tests of any implementation of the Factory interface.
CreateDefaultConfig() ConfigSettings

// CreateConfigSource creates a configuration source based on the given config.
CreateConfigSource(ctx context.Context, params CreateParams, cfg ConfigSettings) (ConfigSource, error)
}

// Factories is a map from types of ConfigSource and the respective Factory for the type.
type Factories map[config.Type]Factory
91 changes: 91 additions & 0 deletions experimental/configsource/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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 configsource

import (
"context"
"fmt"

"github.com/spf13/cast"

"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/configparser"
)

const (
configSourcesKey = "config_sources"
)

// Private error types to help with testability.
type (
errInvalidTypeAndNameKey struct{ error }
errUnknownType struct{ error }
errUnmarshalError struct{ error }
errDuplicateName struct{ error }
)

// Load reads the configuration for ConfigSource objects from the given parser and returns a map
// from the full name of config sources to the respective ConfigSettings.
func Load(_ context.Context, v *config.Parser, factories Factories) (map[string]ConfigSettings, error) {

cfgSrcSettings, err := loadSettings(cast.ToStringMap(v.Get(configSourcesKey)), factories)
if err != nil {
return nil, err
}

return cfgSrcSettings, nil
}

func loadSettings(css map[string]interface{}, factories Factories) (map[string]ConfigSettings, error) {
// Prepare resulting map.
cfgSrcToSettings := make(map[string]ConfigSettings)

// Iterate over extensions and create a config for each.
for key, value := range css {
settingsParser := config.NewParserFromStringMap(cast.ToStringMap(value))

// TODO: expand env vars.

// Decode the key into type and fullName components.
typeStr, fullName, err := configparser.DecodeTypeAndName(key)
if err != nil {
return nil, &errInvalidTypeAndNameKey{fmt.Errorf("invalid %s type and name key %q: %v", configSourcesKey, key, err)}
}

// Find the factory based on "type" that we read from config source.
factory := factories[typeStr]
if factory == nil {
return nil, &errUnknownType{fmt.Errorf("unknown %s type %q for %s", configSourcesKey, typeStr, fullName)}
}

// Create the default config.
cfgSrcSettings := factory.CreateDefaultConfig()
cfgSrcSettings.SetName(fullName)

// Now that the default settings struct is created we can Unmarshal into it
// and it will apply user-defined config on top of the default.
if err := settingsParser.UnmarshalExact(&cfgSrcSettings); err != nil {
return nil, &errUnmarshalError{fmt.Errorf("error reading %s configuration for %s: %v", configSourcesKey, fullName, err)}
}

if cfgSrcToSettings[fullName] != nil {
return nil, &errDuplicateName{fmt.Errorf("duplicate %s name %s", configSourcesKey, fullName)}
}

cfgSrcToSettings[fullName] = cfgSrcSettings
}

return cfgSrcToSettings, nil
}
Loading