Skip to content

Commit

Permalink
Add validate command to validate collector config (#7835)
Browse files Browse the repository at this point in the history
This is bringing back the functionality initially submitted in @Chinwendu20's PR: #6445

Adds a validate method which validates the configuration

Link to tracking Issue: #4613

---------

Signed-off-by: Alex Boten <[email protected]>
Co-authored-by: Maureen <[email protected]>
  • Loading branch information
Alex Boten and Chinwendu20 authored Jun 7, 2023
1 parent 5d032e9 commit 463711f
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 1 deletion.
16 changes: 16 additions & 0 deletions .chloggen/add-dry-run-flag-validate-all-fields.yaml
Original file line number Diff line number Diff line change
@@ -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: service

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Added dry run flag to validate config file without running collector.

# One or more tracking issues or pull requests related to the change
issues: [4671]

# (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:
9 changes: 9 additions & 0 deletions otelcol/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ func (col *Collector) reloadConfiguration(ctx context.Context) error {
return nil
}

func (col *Collector) DryRun(ctx context.Context) error {
cfg, err := col.set.ConfigProvider.Get(ctx, col.set.Factories)
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

return cfg.Validate()
}

// Run starts the collector according to the given configuration, and waits for it to complete.
// Consecutive calls to Run are not allowed, Run shouldn't be called once a collector is shut down.
func (col *Collector) Run(ctx context.Context) error {
Expand Down
19 changes: 19 additions & 0 deletions otelcol/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,25 @@ func TestCollectorClosedStateOnStartUpError(t *testing.T) {
assert.Equal(t, StateClosed, col.GetState())
}

func TestCollectorDryRun(t *testing.T) {
factories, err := nopFactories()
require.NoError(t, err)

cfgProvider, err := NewConfigProvider(newDefaultConfigProviderSettings([]string{filepath.Join("testdata", "otelcol-invalid.yaml")}))
require.NoError(t, err)

// Load a bad config causing startup to fail
set := CollectorSettings{
BuildInfo: component.NewDefaultBuildInfo(),
Factories: factories,
ConfigProvider: cfgProvider,
}
col, err := NewCollector(set)
require.NoError(t, err)

require.Error(t, col.DryRun(context.Background()))
}

func startCollector(ctx context.Context, t *testing.T, col *Collector) *sync.WaitGroup {
wg := &sync.WaitGroup{}
wg.Add(1)
Expand Down
1 change: 1 addition & 0 deletions otelcol/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func NewCommand(set CollectorSettings) *cobra.Command {
},
}
rootCmd.AddCommand(newBuildSubCommand(set))
rootCmd.AddCommand(newValidateSubCommand(set, flagSet))
rootCmd.Flags().AddGoFlagSet(flagSet)
return rootCmd
}
Expand Down
42 changes: 42 additions & 0 deletions otelcol/command_validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package otelcol // import "go.opentelemetry.io/collector/otelcol"

import (
"errors"
"flag"

"github.com/spf13/cobra"
)

// newValidateSubCommand constructs a new validate sub command using the given CollectorSettings.
func newValidateSubCommand(set CollectorSettings, flagSet *flag.FlagSet) *cobra.Command {
validateCmd := &cobra.Command{
Use: "validate",
Short: "Validates the config without running the collector",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
if set.ConfigProvider == nil {
var err error

configFlags := getConfigFlag(flagSet)
if len(configFlags) == 0 {
return errors.New("at least one config flag must be provided")
}

set.ConfigProvider, err = NewConfigProvider(newDefaultConfigProviderSettings(configFlags))
if err != nil {
return err
}
}
col, err := NewCollector(set)
if err != nil {
return err
}
return col.DryRun(cmd.Context())
},
}
validateCmd.Flags().AddGoFlagSet(flagSet)
return validateCmd
}
46 changes: 46 additions & 0 deletions otelcol/command_validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package otelcol

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/converter/expandconverter"
"go.opentelemetry.io/collector/confmap/provider/fileprovider"
"go.opentelemetry.io/collector/featuregate"
)

func TestValidateSubCommandNoConfig(t *testing.T) {
factories, err := nopFactories()
require.NoError(t, err)

cmd := newValidateSubCommand(CollectorSettings{Factories: factories}, flags(featuregate.GlobalRegistry()))
err = cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "at least one config flag must be provided")
}

func TestValidateSubCommandInvalidComponents(t *testing.T) {
factories, err := nopFactories()
require.NoError(t, err)

cfgProvider, err := NewConfigProvider(
ConfigProviderSettings{
ResolverSettings: confmap.ResolverSettings{
URIs: []string{filepath.Join("testdata", "otelcol-invalid-components.yaml")},
Providers: map[string]confmap.Provider{"file": fileprovider.New()},
Converters: []confmap.Converter{expandconverter.New()},
},
})
require.NoError(t, err)

cmd := newValidateSubCommand(CollectorSettings{Factories: factories, ConfigProvider: cfgProvider}, flags(featuregate.GlobalRegistry()))
err = cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "unknown type: \"nosuchprocessor\"")
}
12 changes: 12 additions & 0 deletions otelcol/testdata/otelcol-invalid-components.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
receivers:
nop:
exporters:
nop:
processors:
nosuchprocessor:
service:
pipelines:
traces:
receivers: [nop]
exporters: [nop]
processors: [nop]
7 changes: 6 additions & 1 deletion service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,10 @@ exporters:
extensions:
- zpages
- memory_ballast
```

## How to validate configuration file and return all errors without running collector

```
```bash
./otelcorecol validate --config=file:examples/local/otel-config.yaml
```

0 comments on commit 463711f

Please sign in to comment.