Files
telemetry/otelcol/unmarshal_dry_run_test.go
Sudipto BaralandGitHub 73d7d76e07 Statically validate connector config during dry-run. (#12712)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
As described in
[#8721](https://github.com/open-telemetry/opentelemetry-collector/issues/8721),
the current `validate` command cannot capture improper connector usage
errors during a dry run.

One proposed solution _(as mentioned in [this
comment](https://github.com/open-telemetry/opentelemetry-collector/issues/8721#issuecomment-1775393905))_
is to surface configuration errors by instantiating the service during
the dry run. However, reviewers preferred statically verifying the
errors over instantiating the service (see the [PR #12488
comments](https://github.com/open-telemetry/opentelemetry-collector/pull/12488)).

The validation logic for connectors is located in the
[`service/internal/graph`](https://github.com/open-telemetry/opentelemetry-collector/blob/1daa315455d02bac90185027878d858ba08a0f07/service/internal/graph)
package. In the discussion of [PR
#12681](https://github.com/open-telemetry/opentelemetry-collector/pull/12681),
it was concluded that instantiating the graph is the better approach
(see [this
comment](https://github.com/open-telemetry/opentelemetry-collector/pull/12681#issuecomment-2746258749)).

Finally, this PR uses the `graph.Build()` method to surface
configuration errors during the dry run.

<!-- Issue number if applicable -->
#### Link to tracking issue
Fixes #8721 #12535

<!--Describe what testing was performed and which tests were added.-->
#### Testing

<!--Describe the documentation added.-->
#### Documentation

<!--Please delete paragraphs that you did not use before submitting.-->

---------

Signed-off-by: sudipto baral <[email protected]>
2025-04-09 15:37:07 +00:00

117 lines
2.6 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package otelcol
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/receiver"
)
var _ component.Config = (*Config)(nil)
type ValidateTestConfig struct {
Number int `mapstructure:"number"`
String string `mapstructure:"string"`
}
var genericType component.Type = component.MustNewType("generic")
func NewFactories(_ *testing.T) func() (Factories, error) {
return func() (Factories, error) {
factories, err := nopFactories()
if err != nil {
return Factories{}, err
}
factories.Receivers[genericType] = receiver.NewFactory(
genericType,
func() component.Config {
return &ValidateTestConfig{
Number: 1,
String: "default",
}
})
return factories, nil
}
}
var sampleYAMLConfig = `
receivers:
generic:
number: ${mock:number}
string: ${mock:number}
exporters:
nop:
service:
pipelines:
traces:
receivers: [generic]
exporters: [nop]
`
func TestDryRunWithExpandedValues(t *testing.T) {
tests := []struct {
name string
yamlConfig string
mockMap map[string]string
expectErr bool
}{
{
name: "string that looks like an integer",
yamlConfig: sampleYAMLConfig,
mockMap: map[string]string{
"number": "123",
},
expectErr: true,
},
{
name: "string that looks like a bool",
yamlConfig: sampleYAMLConfig,
mockMap: map[string]string{
"number": "true",
},
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
collector, err := NewCollector(CollectorSettings{
Factories: NewFactories(t),
ConfigProviderSettings: ConfigProviderSettings{
ResolverSettings: confmap.ResolverSettings{
URIs: []string{"file:file"},
DefaultScheme: "mock",
ProviderFactories: []confmap.ProviderFactory{
newFakeProvider("mock", func(_ context.Context, uri string, _ confmap.WatcherFunc) (*confmap.Retrieved, error) {
return confmap.NewRetrievedFromYAML([]byte(tt.mockMap[uri[len("mock:"):]]))
}),
newFakeProvider("file", func(_ context.Context, _ string, _ confmap.WatcherFunc) (*confmap.Retrieved, error) {
return confmap.NewRetrievedFromYAML([]byte(tt.yamlConfig))
}),
},
},
},
SkipSettingGRPCLogger: true,
})
require.NoError(t, err)
err = collector.DryRun(context.Background())
if tt.expectErr {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}