301 lines
8.5 KiB
Go
301 lines
8.5 KiB
Go
// 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 service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"go.uber.org/zap/zapcore"
|
|
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/config"
|
|
"go.opentelemetry.io/collector/config/configtelemetry"
|
|
"go.opentelemetry.io/collector/service/telemetry"
|
|
)
|
|
|
|
var (
|
|
errInvalidRecvConfig = errors.New("invalid receiver config")
|
|
errInvalidExpConfig = errors.New("invalid exporter config")
|
|
errInvalidProcConfig = errors.New("invalid processor config")
|
|
errInvalidExtConfig = errors.New("invalid extension config")
|
|
)
|
|
|
|
type nopRecvConfig struct {
|
|
config.ReceiverSettings
|
|
validateErr error
|
|
}
|
|
|
|
func (nc *nopRecvConfig) Validate() error {
|
|
return nc.validateErr
|
|
}
|
|
|
|
type nopExpConfig struct {
|
|
config.ExporterSettings
|
|
validateErr error
|
|
}
|
|
|
|
func (nc *nopExpConfig) Validate() error {
|
|
return nc.validateErr
|
|
}
|
|
|
|
type nopProcConfig struct {
|
|
config.ProcessorSettings
|
|
validateErr error
|
|
}
|
|
|
|
func (nc *nopProcConfig) Validate() error {
|
|
return nc.validateErr
|
|
}
|
|
|
|
type nopExtConfig struct {
|
|
config.ExtensionSettings
|
|
validateErr error
|
|
}
|
|
|
|
func (nc *nopExtConfig) Validate() error {
|
|
return nc.validateErr
|
|
}
|
|
|
|
func TestConfigValidate(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string // test case name (also file name containing config yaml)
|
|
cfgFn func() *Config
|
|
expected error
|
|
}{
|
|
{
|
|
name: "valid",
|
|
cfgFn: generateConfig,
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "custom-service-telemetrySettings-encoding",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Service.Telemetry.Logs.Encoding = "test_encoding"
|
|
return cfg
|
|
},
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "missing-exporters",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Exporters = nil
|
|
return cfg
|
|
},
|
|
expected: errMissingExporters,
|
|
},
|
|
{
|
|
name: "missing-receivers",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Receivers = nil
|
|
return cfg
|
|
},
|
|
expected: errMissingReceivers,
|
|
},
|
|
{
|
|
name: "invalid-extension-reference",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Service.Extensions = append(cfg.Service.Extensions, component.NewIDWithName("nop", "2"))
|
|
return cfg
|
|
},
|
|
expected: errors.New(`service references extension "nop/2" which does not exist`),
|
|
},
|
|
{
|
|
name: "invalid-receiver-reference",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
pipe := cfg.Service.Pipelines[component.NewID("traces")]
|
|
pipe.Receivers = append(pipe.Receivers, component.NewIDWithName("nop", "2"))
|
|
return cfg
|
|
},
|
|
expected: errors.New(`pipeline "traces" references receiver "nop/2" which does not exist`),
|
|
},
|
|
{
|
|
name: "invalid-processor-reference",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
pipe := cfg.Service.Pipelines[component.NewID("traces")]
|
|
pipe.Processors = append(pipe.Processors, component.NewIDWithName("nop", "2"))
|
|
return cfg
|
|
},
|
|
expected: errors.New(`pipeline "traces" references processor "nop/2" which does not exist`),
|
|
},
|
|
{
|
|
name: "invalid-exporter-reference",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
pipe := cfg.Service.Pipelines[component.NewID("traces")]
|
|
pipe.Exporters = append(pipe.Exporters, component.NewIDWithName("nop", "2"))
|
|
return cfg
|
|
},
|
|
expected: errors.New(`pipeline "traces" references exporter "nop/2" which does not exist`),
|
|
},
|
|
{
|
|
name: "missing-pipeline-receivers",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
pipe := cfg.Service.Pipelines[component.NewID("traces")]
|
|
pipe.Receivers = nil
|
|
return cfg
|
|
},
|
|
expected: errors.New(`pipeline "traces" must have at least one receiver`),
|
|
},
|
|
{
|
|
name: "missing-pipeline-exporters",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
pipe := cfg.Service.Pipelines[component.NewID("traces")]
|
|
pipe.Exporters = nil
|
|
return cfg
|
|
},
|
|
expected: errors.New(`pipeline "traces" must have at least one exporter`),
|
|
},
|
|
{
|
|
name: "missing-pipelines",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Service.Pipelines = nil
|
|
return cfg
|
|
},
|
|
expected: errMissingServicePipelines,
|
|
},
|
|
{
|
|
name: "invalid-receiver-config",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Receivers[component.NewID("nop")] = &nopRecvConfig{
|
|
ReceiverSettings: config.NewReceiverSettings(component.NewID("nop")),
|
|
validateErr: errInvalidRecvConfig,
|
|
}
|
|
return cfg
|
|
},
|
|
expected: fmt.Errorf(`receiver "nop" has invalid configuration: %w`, errInvalidRecvConfig),
|
|
},
|
|
{
|
|
name: "invalid-exporter-config",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Exporters[component.NewID("nop")] = &nopExpConfig{
|
|
ExporterSettings: config.NewExporterSettings(component.NewID("nop")),
|
|
validateErr: errInvalidExpConfig,
|
|
}
|
|
return cfg
|
|
},
|
|
expected: fmt.Errorf(`exporter "nop" has invalid configuration: %w`, errInvalidExpConfig),
|
|
},
|
|
{
|
|
name: "invalid-processor-config",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Processors[component.NewID("nop")] = &nopProcConfig{
|
|
ProcessorSettings: config.NewProcessorSettings(component.NewID("nop")),
|
|
validateErr: errInvalidProcConfig,
|
|
}
|
|
return cfg
|
|
},
|
|
expected: fmt.Errorf(`processor "nop" has invalid configuration: %w`, errInvalidProcConfig),
|
|
},
|
|
{
|
|
name: "invalid-extension-config",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Extensions[component.NewID("nop")] = &nopExtConfig{
|
|
ExtensionSettings: config.NewExtensionSettings(component.NewID("nop")),
|
|
validateErr: errInvalidExtConfig,
|
|
}
|
|
return cfg
|
|
},
|
|
expected: fmt.Errorf(`extension "nop" has invalid configuration: %w`, errInvalidExtConfig),
|
|
},
|
|
{
|
|
name: "invalid-service-pipeline-type",
|
|
cfgFn: func() *Config {
|
|
cfg := generateConfig()
|
|
cfg.Service.Pipelines[component.NewID("wrongtype")] = &ConfigServicePipeline{
|
|
Receivers: []component.ID{component.NewID("nop")},
|
|
Processors: []component.ID{component.NewID("nop")},
|
|
Exporters: []component.ID{component.NewID("nop")},
|
|
}
|
|
return cfg
|
|
},
|
|
expected: errors.New(`unknown pipeline datatype "wrongtype" for wrongtype`),
|
|
},
|
|
}
|
|
|
|
for _, test := range testCases {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
cfg := test.cfgFn()
|
|
assert.Equal(t, test.expected, cfg.Validate())
|
|
})
|
|
}
|
|
}
|
|
|
|
func generateConfig() *Config {
|
|
return &Config{
|
|
Receivers: map[component.ID]component.ReceiverConfig{
|
|
component.NewID("nop"): &nopRecvConfig{
|
|
ReceiverSettings: config.NewReceiverSettings(component.NewID("nop")),
|
|
},
|
|
},
|
|
Exporters: map[component.ID]component.ExporterConfig{
|
|
component.NewID("nop"): &nopExpConfig{
|
|
ExporterSettings: config.NewExporterSettings(component.NewID("nop")),
|
|
},
|
|
},
|
|
Processors: map[component.ID]component.ProcessorConfig{
|
|
component.NewID("nop"): &nopProcConfig{
|
|
ProcessorSettings: config.NewProcessorSettings(component.NewID("nop")),
|
|
},
|
|
},
|
|
Extensions: map[component.ID]component.ExtensionConfig{
|
|
component.NewID("nop"): &nopExtConfig{
|
|
ExtensionSettings: config.NewExtensionSettings(component.NewID("nop")),
|
|
},
|
|
},
|
|
Service: ConfigService{
|
|
Telemetry: telemetry.Config{
|
|
Logs: telemetry.LogsConfig{
|
|
Level: zapcore.DebugLevel,
|
|
Development: true,
|
|
Encoding: "console",
|
|
DisableCaller: true,
|
|
DisableStacktrace: true,
|
|
OutputPaths: []string{"stderr", "./output-logs"},
|
|
ErrorOutputPaths: []string{"stderr", "./error-output-logs"},
|
|
InitialFields: map[string]interface{}{"fieldKey": "filed-value"},
|
|
},
|
|
Metrics: telemetry.MetricsConfig{
|
|
Level: configtelemetry.LevelNormal,
|
|
Address: ":8080",
|
|
},
|
|
},
|
|
Extensions: []component.ID{component.NewID("nop")},
|
|
Pipelines: map[component.ID]*ConfigServicePipeline{
|
|
component.NewID("traces"): {
|
|
Receivers: []component.ID{component.NewID("nop")},
|
|
Processors: []component.ID{component.NewID("nop")},
|
|
Exporters: []component.ID{component.NewID("nop")},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|