1// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package config
16
17import (
18	"errors"
19	"fmt"
20
21	"go.opentelemetry.io/collector/config/configparser"
22)
23
24var (
25	errMissingExporters        = errors.New("no enabled exporters specified in config")
26	errMissingReceivers        = errors.New("no enabled receivers specified in config")
27	errMissingServicePipelines = errors.New("service must have at least one pipeline")
28)
29
30// Config defines the configuration for the various elements of collector or agent.
31type Config struct {
32	Receivers
33	Exporters
34	Processors
35	Extensions
36	Service
37}
38
39var _ validatable = (*Config)(nil)
40
41// Validate returns an error if the config is invalid.
42//
43// This function performs basic validation of configuration. There may be more subtle
44// invalid cases that we currently don't check for but which we may want to add in
45// the future (e.g. disallowing receiving and exporting on the same endpoint).
46func (cfg *Config) Validate() error {
47	// Currently there is no default receiver enabled.
48	// The configuration must specify at least one receiver to be valid.
49	if len(cfg.Receivers) == 0 {
50		return errMissingReceivers
51	}
52
53	// Validate the receiver configuration.
54	for recv, recvCfg := range cfg.Receivers {
55		if err := recvCfg.Validate(); err != nil {
56			return fmt.Errorf("receiver \"%s\" has invalid configuration: %w", recv, err)
57		}
58	}
59
60	// Currently there is no default exporter enabled.
61	// The configuration must specify at least one exporter to be valid.
62	if len(cfg.Exporters) == 0 {
63		return errMissingExporters
64	}
65
66	// Validate the exporter configuration.
67	for exp, expCfg := range cfg.Exporters {
68		if err := expCfg.Validate(); err != nil {
69			return fmt.Errorf("exporter \"%s\" has invalid configuration: %w", exp, err)
70		}
71	}
72
73	// Validate the processor configuration.
74	for proc, procCfg := range cfg.Processors {
75		if err := procCfg.Validate(); err != nil {
76			return fmt.Errorf("processor \"%s\" has invalid configuration: %w", proc, err)
77		}
78	}
79
80	// Validate the extension configuration.
81	for ext, extCfg := range cfg.Extensions {
82		if err := extCfg.Validate(); err != nil {
83			return fmt.Errorf("extension \"%s\" has invalid configuration: %w", ext, err)
84		}
85	}
86
87	// Check that all enabled extensions in the service are configured.
88	if err := cfg.validateServiceExtensions(); err != nil {
89		return err
90	}
91
92	// Check that all pipelines have at least one receiver and one exporter, and they reference
93	// only configured components.
94	return cfg.validateServicePipelines()
95}
96
97func (cfg *Config) validateServiceExtensions() error {
98	// Validate extensions.
99	for _, ref := range cfg.Service.Extensions {
100		// Check that the name referenced in the Service extensions exists in the top-level extensions.
101		if cfg.Extensions[ref] == nil {
102			return fmt.Errorf("service references extension %q which does not exist", ref)
103		}
104	}
105
106	return nil
107}
108
109func (cfg *Config) validateServicePipelines() error {
110	// Must have at least one pipeline.
111	if len(cfg.Service.Pipelines) == 0 {
112		return errMissingServicePipelines
113	}
114
115	// Validate pipelines.
116	for _, pipeline := range cfg.Service.Pipelines {
117		// Validate pipeline has at least one receiver.
118		if len(pipeline.Receivers) == 0 {
119			return fmt.Errorf("pipeline %q must have at least one receiver", pipeline.Name)
120		}
121
122		// Validate pipeline receiver name references.
123		for _, ref := range pipeline.Receivers {
124			// Check that the name referenced in the pipeline's receivers exists in the top-level receivers.
125			if cfg.Receivers[ref] == nil {
126				return fmt.Errorf("pipeline %q references receiver %q which does not exist", pipeline.Name, ref)
127			}
128		}
129
130		// Validate pipeline processor name references.
131		for _, ref := range pipeline.Processors {
132			// Check that the name referenced in the pipeline's processors exists in the top-level processors.
133			if cfg.Processors[ref] == nil {
134				return fmt.Errorf("pipeline %q references processor %q which does not exist", pipeline.Name, ref)
135			}
136		}
137
138		// Validate pipeline has at least one exporter.
139		if len(pipeline.Exporters) == 0 {
140			return fmt.Errorf("pipeline %q must have at least one exporter", pipeline.Name)
141		}
142
143		// Validate pipeline exporter name references.
144		for _, ref := range pipeline.Exporters {
145			// Check that the name referenced in the pipeline's Exporters exists in the top-level Exporters.
146			if cfg.Exporters[ref] == nil {
147				return fmt.Errorf("pipeline %q references exporter %q which does not exist", pipeline.Name, ref)
148			}
149		}
150	}
151	return nil
152}
153
154// Service defines the configurable components of the service.
155type Service struct {
156	// Extensions are the ordered list of extensions configured for the service.
157	Extensions []ComponentID
158
159	// Pipelines are the set of data pipelines configured for the service.
160	Pipelines Pipelines
161}
162
163// Type is the component type as it is used in the config.
164type Type string
165
166// validatable defines the interface for the configuration validation.
167type validatable interface {
168	// Validate validates the configuration and returns an error if invalid.
169	Validate() error
170}
171
172// CustomUnmarshable defines an optional interface for custom configuration unmarshaling.
173// A configuration struct can implement this interface to override the default unmarshaling.
174type CustomUnmarshable interface {
175	// Unmarshal is a function that un-marshals a Parser into the unmarshable struct in a custom way.
176	// componentSection *Parser
177	//   The config for this specific component. May be nil or empty if no config available.
178	Unmarshal(componentSection *configparser.Parser) error
179}
180
181// DataType is the data type that is supported for collection. We currently support
182// collecting metrics, traces and logs, this can expand in the future.
183type DataType string
184
185// Currently supported data types. Add new data types here when new types are supported in the future.
186const (
187	// TracesDataType is the data type tag for traces.
188	TracesDataType DataType = "traces"
189
190	// MetricsDataType is the data type tag for metrics.
191	MetricsDataType DataType = "metrics"
192
193	// LogsDataType is the data type tag for logs.
194	LogsDataType DataType = "logs"
195)
196
197// Pipeline defines a single pipeline.
198type Pipeline struct {
199	Name       string
200	InputType  DataType
201	Receivers  []ComponentID
202	Processors []ComponentID
203	Exporters  []ComponentID
204}
205
206// Pipelines is a map of names to Pipelines.
207type Pipelines map[string]*Pipeline
208