From 4b5bac6cfd74d0be66ec48ceaee38a4cf92e1ed2 Mon Sep 17 00:00:00 2001 From: Dan Jaglowski Date: Thu, 8 Sep 2022 10:37:19 -0400 Subject: [PATCH] Prototype "connectors", a new type of pipeline component --- .chloggen/connectors-prototype-v2.yaml | 22 + .github/dependabot.yml | 8 + .gitignore | 1 + cmd/builder/internal/builder/config.go | 14 +- cmd/builder/internal/builder/main_test.go | 9 +- .../builder/templates/components.go.tmpl | 12 + .../builder/templates/components_test.go.tmpl | 3 + .../internal/builder/templates/go.mod.tmpl | 7 + cmd/builder/internal/command.go | 1 + cmd/builder/internal/config/default.yaml | 5 + cmd/builder/test/core.builder.yaml | 15 +- cmd/builder/test/test.sh | 6 +- cmd/otelcorecol/builder-config.yaml | 7 + cmd/otelcorecol/components.go | 10 + cmd/otelcorecol/components_test.go | 3 + cmd/otelcorecol/go.mod | 7 + cmd/otelcorecol/go.sum | 3 + component/component.go | 1 + component/componenttest/nop_connector.go | 130 +++ component/componenttest/nop_connector_test.go | 92 ++ component/componenttest/nop_factories.go | 4 + component/componenttest/nop_factories_test.go | 53 + component/connector.go | 516 +++++++++ component/connector_test.go | 225 ++++ component/factories.go | 17 + component/factories_test.go | 38 + config/connector.go | 51 + connector/countconnector/Makefile | 1 + connector/countconnector/README.md | 18 + connector/countconnector/count.go | 205 ++++ connector/countconnector/count_test.go | 127 ++ connector/countconnector/doc.go | 16 + connector/countconnector/go.mod | 51 + connector/countconnector/go.sum | 441 +++++++ connector/nopconnector/Makefile | 1 + connector/nopconnector/README.md | 21 + connector/nopconnector/doc.go | 16 + connector/nopconnector/go.mod | 51 + connector/nopconnector/go.sum | 441 +++++++ connector/nopconnector/nop.go | 146 +++ connector/nopconnector/nop_test.go | 73 ++ go.mod | 1 + go.sum | 3 + service/config.go | 52 +- service/config_provider.go | 1 + service/config_test.go | 101 ++ service/host.go | 2 +- service/internal/components/constants.go | 20 +- .../internal/configunmarshaler/connectors.go | 72 ++ .../configunmarshaler/connectors_test.go | 114 ++ service/internal/pipelines/graph.go | 554 +++++++++ service/internal/pipelines/graph_test.go | 1018 +++++++++++++++++ service/internal/pipelines/nodes.go | 491 ++++++++ service/internal/pipelines/pipelines.go | 73 +- service/internal/pipelines/pipelines_test.go | 146 ++- .../not_allowed_conn_omit_exp_logs.yaml | 15 + .../not_allowed_conn_omit_exp_metrics.yaml | 15 + .../not_allowed_conn_omit_exp_traces.yaml | 15 + .../not_allowed_conn_omit_recv_logs.yaml | 15 + .../not_allowed_conn_omit_recv_metrics.yaml | 15 + .../not_allowed_conn_omit_recv_traces.yaml | 15 + .../testdata/not_allowed_deep_cycle_logs.yaml | 34 + .../not_allowed_deep_cycle_metrics.yaml | 34 + .../not_allowed_deep_cycle_multi_signal.yaml | 47 + .../not_allowed_deep_cycle_traces.yaml | 34 + .../not_allowed_simple_cycle_logs.yaml | 15 + .../not_allowed_simple_cycle_metrics.yaml | 15 + .../not_allowed_simple_cycle_traces.yaml | 15 + .../not_supported_connector_logs_logs.yaml | 15 + .../not_supported_connector_logs_metrics.yaml | 15 + .../not_supported_connector_logs_traces.yaml | 15 + .../not_supported_connector_metrics_logs.yaml | 15 + ...t_supported_connector_metrics_metrics.yaml | 15 + ...ot_supported_connector_metrics_traces.yaml | 15 + .../not_supported_connector_traces_logs.yaml | 15 + ...ot_supported_connector_traces_metrics.yaml | 15 + ...not_supported_connector_traces_traces.yaml | 15 + .../pipelines_conn_fork_merge_logs.yaml | 34 + .../pipelines_conn_fork_merge_metrics.yaml | 34 + .../pipelines_conn_fork_merge_traces.yaml | 34 + .../testdata/pipelines_conn_matrix.yaml | 39 + .../testdata/pipelines_conn_simple_logs.yaml | 22 + .../pipelines_conn_simple_metrics.yaml | 22 + .../pipelines_conn_simple_traces.yaml | 22 + .../pipelines_conn_translate_from_logs.yaml | 26 + ...pipelines_conn_translate_from_metrics.yaml | 26 + .../pipelines_conn_translate_from_traces.yaml | 26 + .../testdata/unknown_connector_config.yaml | 15 + .../testdata/unknown_connector_factory.yaml | 15 + .../testcomponents/example_connector.go | 158 +++ .../testcomponents/example_connector_test.go | 36 + .../testcomponents/example_exporter.go | 18 +- .../testcomponents/example_exporter_test.go | 21 +- .../testcomponents/example_factories.go | 43 + .../testcomponents/example_processor.go | 9 +- .../testcomponents/example_processor_test.go | 8 +- .../testcomponents/example_receiver.go | 9 +- .../testcomponents/example_receiver_test.go | 8 +- service/service.go | 29 +- service/servicetest/configprovider_test.go | 4 + service/servicetest/testdata/config.yaml | 3 + service/unmarshaler.go | 2 + versions.yaml | 2 + 103 files changed, 6504 insertions(+), 96 deletions(-) create mode 100755 .chloggen/connectors-prototype-v2.yaml create mode 100644 component/componenttest/nop_connector.go create mode 100644 component/componenttest/nop_connector_test.go create mode 100644 component/componenttest/nop_factories_test.go create mode 100644 component/connector.go create mode 100644 component/connector_test.go create mode 100644 config/connector.go create mode 100644 connector/countconnector/Makefile create mode 100644 connector/countconnector/README.md create mode 100644 connector/countconnector/count.go create mode 100644 connector/countconnector/count_test.go create mode 100644 connector/countconnector/doc.go create mode 100644 connector/countconnector/go.mod create mode 100644 connector/countconnector/go.sum create mode 100644 connector/nopconnector/Makefile create mode 100644 connector/nopconnector/README.md create mode 100644 connector/nopconnector/doc.go create mode 100644 connector/nopconnector/go.mod create mode 100644 connector/nopconnector/go.sum create mode 100644 connector/nopconnector/nop.go create mode 100644 connector/nopconnector/nop_test.go create mode 100644 service/internal/configunmarshaler/connectors.go create mode 100644 service/internal/configunmarshaler/connectors_test.go create mode 100644 service/internal/pipelines/graph.go create mode 100644 service/internal/pipelines/graph_test.go create mode 100644 service/internal/pipelines/nodes.go create mode 100644 service/internal/pipelines/testdata/not_allowed_conn_omit_exp_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_conn_omit_exp_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_conn_omit_exp_traces.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_conn_omit_recv_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_conn_omit_recv_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_conn_omit_recv_traces.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_deep_cycle_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_deep_cycle_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_deep_cycle_multi_signal.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_deep_cycle_traces.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_simple_cycle_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_simple_cycle_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_allowed_simple_cycle_traces.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_logs_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_logs_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_logs_traces.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_metrics_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_metrics_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_metrics_traces.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_traces_logs.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_traces_metrics.yaml create mode 100644 service/internal/pipelines/testdata/not_supported_connector_traces_traces.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_fork_merge_logs.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_fork_merge_metrics.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_fork_merge_traces.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_matrix.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_simple_logs.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_simple_metrics.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_simple_traces.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_translate_from_logs.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_translate_from_metrics.yaml create mode 100644 service/internal/pipelines/testdata/pipelines_conn_translate_from_traces.yaml create mode 100644 service/internal/pipelines/testdata/unknown_connector_config.yaml create mode 100644 service/internal/pipelines/testdata/unknown_connector_factory.yaml create mode 100644 service/internal/testcomponents/example_connector.go create mode 100644 service/internal/testcomponents/example_connector_test.go diff --git a/.chloggen/connectors-prototype-v2.yaml b/.chloggen/connectors-prototype-v2.yaml new file mode 100755 index 000000000000..f86d63d6123f --- /dev/null +++ b/.chloggen/connectors-prototype-v2.yaml @@ -0,0 +1,22 @@ +# 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: connectors + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add "connectors", a new type of pipeline component + +# One or more tracking issues or pull requests related to the change +issues: [2336] + +# (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: | + - Connectors connect pipelines. A connector acts as exporter and a receiver working together. + For example, a `nopconnector`` may be used to replicate signals by exporting data from one + pipeline and receiving the data on two other pipelines, which can each process a copy of the data + in different ways. + Connectors can also derive one signal from another. For example, a `countconnector` can be used as + an exporter on a logs pipeline and emit metrics, describing the number of logs, onto a metrics pipeline. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 14e9da99c764..3f0e0c726801 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,6 +26,14 @@ updates: directory: "/consumer" schedule: interval: "weekly" + - package-ecosystem: "gomod" + directory: "/connector/countconnector" + schedule: + interval: "weekly" + - package-ecosystem: "gomod" + directory: "/connector/nopconnector" + schedule: + interval: "weekly" - package-ecosystem: "gomod" directory: "/exporter/loggingexporter" schedule: diff --git a/.gitignore b/.gitignore index 73d12f27d63e..bf64de69c201 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ dist/ +local/ # GoLand IDEA /.idea/ diff --git a/cmd/builder/internal/builder/config.go b/cmd/builder/internal/builder/config.go index 323854e167ce..fc9d1c2e5dd5 100644 --- a/cmd/builder/internal/builder/config.go +++ b/cmd/builder/internal/builder/config.go @@ -42,6 +42,7 @@ type Config struct { Extensions []Module `mapstructure:"extensions"` Receivers []Module `mapstructure:"receivers"` Processors []Module `mapstructure:"processors"` + Connectors []Module `mapstructure:"connectors"` Replaces []string `mapstructure:"replaces"` Excludes []string `mapstructure:"excludes"` } @@ -90,7 +91,13 @@ func NewDefaultConfig() Config { // Validate checks whether the current configuration is valid func (c *Config) Validate() error { - return multierr.Combine(validateModules(c.Extensions), validateModules(c.Receivers), validateModules(c.Exporters), validateModules(c.Processors)) + return multierr.Combine( + validateModules(c.Extensions), + validateModules(c.Receivers), + validateModules(c.Exporters), + validateModules(c.Processors), + validateModules(c.Connectors), + ) } // SetGoPath sets go path @@ -133,6 +140,11 @@ func (c *Config) ParseModules() error { return err } + c.Connectors, err = parseModules(c.Connectors) + if err != nil { + return err + } + return nil } diff --git a/cmd/builder/internal/builder/main_test.go b/cmd/builder/internal/builder/main_test.go index b8131aa09631..8f1b6d8adeab 100644 --- a/cmd/builder/internal/builder/main_test.go +++ b/cmd/builder/internal/builder/main_test.go @@ -15,6 +15,8 @@ package builder import ( + "fmt" + "path/filepath" "runtime" "testing" "time" @@ -49,8 +51,11 @@ func TestGenerateAndCompileDefault(t *testing.T) { cfg := NewDefaultConfig() cfg.Distribution.OutputPath = t.TempDir() - // we override this version, otherwise this would break during releases - cfg.Distribution.OtelColVersion = "0.52.0" + _, thisFile, _, _ := runtime.Caller(0) + workspaceDir := filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(thisFile))))) + + cfg.Replaces = append(cfg.Replaces, fmt.Sprintf("go.opentelemetry.io/collector => %s", workspaceDir)) + cfg.Replaces = append(cfg.Replaces, fmt.Sprintf("go.opentelemetry.io/collector/component => %s/component", workspaceDir)) assert.NoError(t, cfg.Validate()) assert.NoError(t, cfg.SetGoPath()) diff --git a/cmd/builder/internal/builder/templates/components.go.tmpl b/cmd/builder/internal/builder/templates/components.go.tmpl index b213dd7bb1db..30d073274a17 100644 --- a/cmd/builder/internal/builder/templates/components.go.tmpl +++ b/cmd/builder/internal/builder/templates/components.go.tmpl @@ -4,6 +4,9 @@ package main import ( "go.opentelemetry.io/collector/component" + {{- range .Connectors}} + {{.Name}} "{{.Import}}" + {{- end}} {{- range .Exporters}} {{.Name}} "{{.Import}}" {{- end}} @@ -31,6 +34,15 @@ func components() (component.Factories, error) { return component.Factories{}, err } + factories.Connectors, err = component.MakeConnectorFactoryMap( + {{- range .Connectors}} + {{.Name}}.NewFactory(), + {{- end}} + ) + if err != nil { + return component.Factories{}, err + } + factories.Receivers, err = component.MakeReceiverFactoryMap( {{- range .Receivers}} {{.Name}}.NewFactory(), diff --git a/cmd/builder/internal/builder/templates/components_test.go.tmpl b/cmd/builder/internal/builder/templates/components_test.go.tmpl index 09f867d84f8c..19cbf5990d1e 100644 --- a/cmd/builder/internal/builder/templates/components_test.go.tmpl +++ b/cmd/builder/internal/builder/templates/components_test.go.tmpl @@ -26,4 +26,7 @@ func TestValidateConfigs(t *testing.T) { for _, factory := range factories.Extensions { assert.NoError(t, componenttest.CheckConfigStruct(factory.CreateDefaultConfig())) } + for _, factory := range factories.Connectors { + assert.NoError(t, componenttest.CheckConfigStruct(factory.CreateDefaultConfig())) + } } diff --git a/cmd/builder/internal/builder/templates/go.mod.tmpl b/cmd/builder/internal/builder/templates/go.mod.tmpl index cf7da63559f6..511fa1d7042c 100644 --- a/cmd/builder/internal/builder/templates/go.mod.tmpl +++ b/cmd/builder/internal/builder/templates/go.mod.tmpl @@ -17,6 +17,9 @@ require ( {{- range .Processors}} {{if .GoMod}}{{.GoMod}}{{end}} {{- end}} + {{- range .Connectors}} + {{if .GoMod}}{{.GoMod}}{{end}} + {{- end}} go.opentelemetry.io/collector v{{.Distribution.OtelColVersion}} ) @@ -32,6 +35,10 @@ require ( {{- range .Processors}} {{if ne .Path ""}}replace {{.GoMod}} => {{.Path}}{{end}} {{- end}} +{{- range .Connectors}} +{{if ne .Path ""}}replace {{.GoMod}} => {{.Path}}{{end}} +{{- end}} + {{- range .Replaces}} replace {{.}} {{- end}} diff --git a/cmd/builder/internal/command.go b/cmd/builder/internal/command.go index 76046224029e..725cf3cd2ce9 100644 --- a/cmd/builder/internal/command.go +++ b/cmd/builder/internal/command.go @@ -166,6 +166,7 @@ func applyCfgFromFile(flags *flag.FlagSet, cfgFromFile builder.Config) { cfg.Extensions = cfgFromFile.Extensions cfg.Receivers = cfgFromFile.Receivers cfg.Processors = cfgFromFile.Processors + cfg.Connectors = cfgFromFile.Connectors cfg.Replaces = cfgFromFile.Replaces cfg.Excludes = cfgFromFile.Excludes diff --git a/cmd/builder/internal/config/default.yaml b/cmd/builder/internal/config/default.yaml index 9e231f1e9e1b..bd648b8301df 100644 --- a/cmd/builder/internal/config/default.yaml +++ b/cmd/builder/internal/config/default.yaml @@ -17,4 +17,9 @@ extensions: processors: - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.65.0 - gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.65.0 +connectors: + - import: go.opentelemetry.io/collector/connector/countconnector + gomod: go.opentelemetry.io/collector/connector/countconnector v0.0.0 + - import: go.opentelemetry.io/collector/connector/nopconnector + gomod: go.opentelemetry.io/collector/connector/nopconnector v0.0.0 diff --git a/cmd/builder/test/core.builder.yaml b/cmd/builder/test/core.builder.yaml index 1f13baa3d03a..c83f9366b89d 100644 --- a/cmd/builder/test/core.builder.yaml +++ b/cmd/builder/test/core.builder.yaml @@ -1,15 +1,22 @@ dist: module: go.opentelemetry.io/collector/builder/test/core - otelcol_version: 0.44.0 + otelcol_version: 0.64.1 extensions: - import: go.opentelemetry.io/collector/extension/zpagesextension - gomod: go.opentelemetry.io/collector v0.44.0 + gomod: go.opentelemetry.io/collector v0.64.1 + path: ${WORKSPACE_DIR} receivers: - import: go.opentelemetry.io/collector/receiver/otlpreceiver - gomod: go.opentelemetry.io/collector v0.44.0 + gomod: go.opentelemetry.io/collector v0.64.1 + path: ${WORKSPACE_DIR} exporters: - import: go.opentelemetry.io/collector/exporter/loggingexporter - gomod: go.opentelemetry.io/collector v0.44.0 + gomod: go.opentelemetry.io/collector v0.64.1 + path: ${WORKSPACE_DIR} + +replaces: + - go.opentelemetry.io/collector => ${WORKSPACE_DIR} + - go.opentelemetry.io/collector/component => ${WORKSPACE_DIR}/component diff --git a/cmd/builder/test/test.sh b/cmd/builder/test/test.sh index 434102ea46f4..b9db313894fd 100755 --- a/cmd/builder/test/test.sh +++ b/cmd/builder/test/test.sh @@ -1,4 +1,6 @@ #!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +export WORKSPACE_DIR=$( cd -- "$( dirname $(dirname $(dirname -- "${SCRIPT_DIR}")) )" &> /dev/null && pwd ) GOBIN=$(go env GOBIN) if [[ "$GO" == "" ]]; then @@ -25,7 +27,9 @@ test_build_config() { echo "Starting test '${test}' at `date`" >> "${out}/test.log" - go run . --go "${GOBIN}" --config "$build_config" --output-path "${out}" --name otelcol-built-test > "${out}/builder.log" 2>&1 + final_build_config=$(basename ${build_config}) + envsubst < "$build_config" > "${out}/${final_build_config}" + go run . --go "${GOBIN}" --config "${out}/${final_build_config}" --output-path "${out}" --name otelcol-built-test > "${out}/builder.log" 2>&1 if [ $? != 0 ]; then echo "❌ FAIL ${test}. Failed to compile the test ${test}. Build logs:" diff --git a/cmd/otelcorecol/builder-config.yaml b/cmd/otelcorecol/builder-config.yaml index fc4bff39a93b..94dddc8e9691 100644 --- a/cmd/otelcorecol/builder-config.yaml +++ b/cmd/otelcorecol/builder-config.yaml @@ -17,11 +17,18 @@ extensions: processors: - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.65.0 - gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.65.0 +connectors: + - import: go.opentelemetry.io/collector/connector/countconnector + gomod: go.opentelemetry.io/collector/connector/countconnector v0.0.0 + - import: go.opentelemetry.io/collector/connector/nopconnector + gomod: go.opentelemetry.io/collector/connector/nopconnector v0.0.0 replaces: - go.opentelemetry.io/collector => ../../ - go.opentelemetry.io/collector/component => ../../component - go.opentelemetry.io/collector/consumer => ../../consumer + - go.opentelemetry.io/collector/connector/countconnector => ../../connector/countconnector + - go.opentelemetry.io/collector/connector/nopconnector => ../../connector/nopconnector - go.opentelemetry.io/collector/exporter/loggingexporter => ../../exporter/loggingexporter - go.opentelemetry.io/collector/exporter/otlpexporter => ../../exporter/otlpexporter - go.opentelemetry.io/collector/exporter/otlphttpexporter => ../../exporter/otlphttpexporter diff --git a/cmd/otelcorecol/components.go b/cmd/otelcorecol/components.go index 9156edaad1ae..9e120ec95ea4 100644 --- a/cmd/otelcorecol/components.go +++ b/cmd/otelcorecol/components.go @@ -4,6 +4,8 @@ package main import ( "go.opentelemetry.io/collector/component" + countconnector "go.opentelemetry.io/collector/connector/countconnector" + nopconnector "go.opentelemetry.io/collector/connector/nopconnector" loggingexporter "go.opentelemetry.io/collector/exporter/loggingexporter" otlpexporter "go.opentelemetry.io/collector/exporter/otlpexporter" otlphttpexporter "go.opentelemetry.io/collector/exporter/otlphttpexporter" @@ -26,6 +28,14 @@ func components() (component.Factories, error) { return component.Factories{}, err } + factories.Connectors, err = component.MakeConnectorFactoryMap( + countconnector.NewFactory(), + nopconnector.NewFactory(), + ) + if err != nil { + return component.Factories{}, err + } + factories.Receivers, err = component.MakeReceiverFactoryMap( otlpreceiver.NewFactory(), ) diff --git a/cmd/otelcorecol/components_test.go b/cmd/otelcorecol/components_test.go index 09f867d84f8c..19cbf5990d1e 100644 --- a/cmd/otelcorecol/components_test.go +++ b/cmd/otelcorecol/components_test.go @@ -26,4 +26,7 @@ func TestValidateConfigs(t *testing.T) { for _, factory := range factories.Extensions { assert.NoError(t, componenttest.CheckConfigStruct(factory.CreateDefaultConfig())) } + for _, factory := range factories.Connectors { + assert.NoError(t, componenttest.CheckConfigStruct(factory.CreateDefaultConfig())) + } } diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index f8e601f94721..fb2def510f65 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -8,6 +8,8 @@ require ( github.com/stretchr/testify v1.8.1 go.opentelemetry.io/collector v0.65.0 go.opentelemetry.io/collector/component v0.65.0 + go.opentelemetry.io/collector/connector/countconnector v0.0.0 + go.opentelemetry.io/collector/connector/nopconnector v0.0.0 go.opentelemetry.io/collector/exporter/loggingexporter v0.65.0 go.opentelemetry.io/collector/exporter/otlpexporter v0.65.0 go.opentelemetry.io/collector/exporter/otlphttpexporter v0.65.0 @@ -82,6 +84,7 @@ require ( go.uber.org/zap v1.23.0 // indirect golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect golang.org/x/text v0.4.0 // indirect + gonum.org/v1/gonum v0.12.0 // indirect google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect @@ -95,6 +98,10 @@ replace go.opentelemetry.io/collector/component => ../../component replace go.opentelemetry.io/collector/consumer => ../../consumer +replace go.opentelemetry.io/collector/connector/countconnector => ../../connector/countconnector + +replace go.opentelemetry.io/collector/connector/nopconnector => ../../connector/nopconnector + replace go.opentelemetry.io/collector/exporter/loggingexporter => ../../exporter/loggingexporter replace go.opentelemetry.io/collector/exporter/otlpexporter => ../../exporter/otlpexporter diff --git a/cmd/otelcorecol/go.sum b/cmd/otelcorecol/go.sum index 6ed6f70d4552..c33e053c2e0b 100644 --- a/cmd/otelcorecol/go.sum +++ b/cmd/otelcorecol/go.sum @@ -467,6 +467,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -674,6 +675,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/component/component.go b/component/component.go index e651f56595f2..e393f1cf7755 100644 --- a/component/component.go +++ b/component/component.go @@ -104,6 +104,7 @@ const ( KindProcessor KindExporter KindExtension + KindConnector ) // StabilityLevel represents the stability level of the component created by the factory. diff --git a/component/componenttest/nop_connector.go b/component/componenttest/nop_connector.go new file mode 100644 index 000000000000..58da54ab625e --- /dev/null +++ b/component/componenttest/nop_connector.go @@ -0,0 +1,130 @@ +// 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 componenttest // import "go.opentelemetry.io/collector/component/componenttest" + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +// NewNopConnectorCreateSettings returns a new nop settings for Create*Connector functions. +func NewNopConnectorCreateSettings() component.ConnectorCreateSettings { + return component.ConnectorCreateSettings{ + TelemetrySettings: NewNopTelemetrySettings(), + BuildInfo: component.NewDefaultBuildInfo(), + } +} + +type nopConnectorConfig struct { + config.ConnectorSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct +} + +// NewNopConnectorFactory returns a component.ConnectorFactory that constructs nop processors. +func NewNopConnectorFactory() component.ConnectorFactory { + return component.NewConnectorFactory( + "nop", + func() component.ConnectorConfig { + return &nopConnectorConfig{ + ConnectorSettings: config.NewConnectorSettings(component.NewID("nop")), + } + }, + component.WithTracesToTracesConnector(createTracesToTracesConnector, component.StabilityLevelStable), + component.WithTracesToMetricsConnector(createTracesToMetricsConnector, component.StabilityLevelStable), + component.WithTracesToLogsConnector(createTracesToLogsConnector, component.StabilityLevelStable), + component.WithMetricsToTracesConnector(createMetricsToTracesConnector, component.StabilityLevelStable), + component.WithMetricsToMetricsConnector(createMetricsToMetricsConnector, component.StabilityLevelStable), + component.WithMetricsToLogsConnector(createMetricsToLogsConnector, component.StabilityLevelStable), + component.WithLogsToTracesConnector(createLogsToTracesConnector, component.StabilityLevelStable), + component.WithLogsToMetricsConnector(createLogsToMetricsConnector, component.StabilityLevelStable), + component.WithLogsToLogsConnector(createLogsToLogsConnector, component.StabilityLevelStable), + ) +} + +func createTracesToTracesConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.TracesToTracesConnector, error) { + return nopConnectorInstance, nil +} +func createTracesToMetricsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.TracesToMetricsConnector, error) { + return nopConnectorInstance, nil +} +func createTracesToLogsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.TracesToLogsConnector, error) { + return nopConnectorInstance, nil +} + +func createMetricsToTracesConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.MetricsToTracesConnector, error) { + return nopConnectorInstance, nil +} +func createMetricsToMetricsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.MetricsToMetricsConnector, error) { + return nopConnectorInstance, nil +} +func createMetricsToLogsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.MetricsToLogsConnector, error) { + return nopConnectorInstance, nil +} + +func createLogsToTracesConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.LogsToTracesConnector, error) { + return nopConnectorInstance, nil +} +func createLogsToMetricsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.LogsToMetricsConnector, error) { + return nopConnectorInstance, nil +} +func createLogsToLogsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.LogsToLogsConnector, error) { + return nopConnectorInstance, nil +} + +var nopConnectorInstance = &nopConnector{ + Consumer: consumertest.NewNop(), +} + +// nopConnector stores consumed traces and metrics for testing purposes. +type nopConnector struct { + nopComponent + consumertest.Consumer +} + +func (c *nopConnector) ConsumeTracesToTraces(ctx context.Context, td ptrace.Traces) error { + return nil +} +func (c *nopConnector) ConsumeTracesToMetrics(ctx context.Context, td ptrace.Traces) error { + return nil +} +func (c *nopConnector) ConsumeTracesToLogs(ctx context.Context, td ptrace.Traces) error { + return nil +} + +func (c *nopConnector) ConsumeMetricsToTraces(ctx context.Context, md pmetric.Metrics) error { + return nil +} +func (c *nopConnector) ConsumeMetricsToMetrics(ctx context.Context, md pmetric.Metrics) error { + return nil +} +func (c *nopConnector) ConsumeMetricsToLogs(ctx context.Context, md pmetric.Metrics) error { + return nil +} + +func (c *nopConnector) ConsumeLogsToTraces(ctx context.Context, ld plog.Logs) error { + return nil +} +func (c *nopConnector) ConsumeLogsToMetrics(ctx context.Context, ld plog.Logs) error { + return nil +} +func (c *nopConnector) ConsumeLogsToLogs(ctx context.Context, ld plog.Logs) error { + return nil +} diff --git a/component/componenttest/nop_connector_test.go b/component/componenttest/nop_connector_test.go new file mode 100644 index 000000000000..13e7f0979ebb --- /dev/null +++ b/component/componenttest/nop_connector_test.go @@ -0,0 +1,92 @@ +// 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 componenttest + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +func TestNewNopConnectorFactory(t *testing.T) { + factory := NewNopConnectorFactory() + require.NotNil(t, factory) + assert.Equal(t, component.Type("nop"), factory.Type()) + cfg := factory.CreateDefaultConfig() + assert.Equal(t, &nopConnectorConfig{ConnectorSettings: config.NewConnectorSettings(component.NewID("nop"))}, cfg) + + tracesToTraces, err := factory.CreateTracesToTracesConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, tracesToTraces.Start(context.Background(), NewNopHost())) + assert.NoError(t, tracesToTraces.ConsumeTracesToTraces(context.Background(), ptrace.NewTraces())) + assert.NoError(t, tracesToTraces.Shutdown(context.Background())) + + tracesToMetrics, err := factory.CreateTracesToMetricsConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, tracesToMetrics.Start(context.Background(), NewNopHost())) + assert.NoError(t, tracesToMetrics.ConsumeTracesToMetrics(context.Background(), ptrace.NewTraces())) + assert.NoError(t, tracesToMetrics.Shutdown(context.Background())) + + tracesToLogs, err := factory.CreateTracesToLogsConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, tracesToLogs.Start(context.Background(), NewNopHost())) + assert.NoError(t, tracesToLogs.ConsumeTracesToLogs(context.Background(), ptrace.NewTraces())) + assert.NoError(t, tracesToLogs.Shutdown(context.Background())) + + metricsToTraces, err := factory.CreateMetricsToTracesConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, metricsToTraces.Start(context.Background(), NewNopHost())) + assert.NoError(t, metricsToTraces.ConsumeMetricsToTraces(context.Background(), pmetric.NewMetrics())) + assert.NoError(t, metricsToTraces.Shutdown(context.Background())) + + metricsToMetrics, err := factory.CreateMetricsToMetricsConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, metricsToMetrics.Start(context.Background(), NewNopHost())) + assert.NoError(t, metricsToMetrics.ConsumeMetricsToMetrics(context.Background(), pmetric.NewMetrics())) + assert.NoError(t, metricsToMetrics.Shutdown(context.Background())) + + metricsToLogs, err := factory.CreateMetricsToLogsConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, metricsToLogs.Start(context.Background(), NewNopHost())) + assert.NoError(t, metricsToLogs.ConsumeMetricsToLogs(context.Background(), pmetric.NewMetrics())) + assert.NoError(t, metricsToLogs.Shutdown(context.Background())) + + logsToTraces, err := factory.CreateLogsToTracesConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, logsToTraces.Start(context.Background(), NewNopHost())) + assert.NoError(t, logsToTraces.ConsumeLogsToTraces(context.Background(), plog.NewLogs())) + assert.NoError(t, logsToTraces.Shutdown(context.Background())) + + logsToMetrics, err := factory.CreateLogsToMetricsConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, logsToMetrics.Start(context.Background(), NewNopHost())) + assert.NoError(t, logsToMetrics.ConsumeLogsToMetrics(context.Background(), plog.NewLogs())) + assert.NoError(t, logsToMetrics.Shutdown(context.Background())) + + logsToLogs, err := factory.CreateLogsToLogsConnector(context.Background(), NewNopConnectorCreateSettings(), cfg, consumertest.NewNop()) + require.NoError(t, err) + assert.NoError(t, logsToLogs.Start(context.Background(), NewNopHost())) + assert.NoError(t, logsToLogs.ConsumeLogsToLogs(context.Background(), plog.NewLogs())) + assert.NoError(t, logsToLogs.Shutdown(context.Background())) +} diff --git a/component/componenttest/nop_factories.go b/component/componenttest/nop_factories.go index 03b026cc6992..72f7a3eea041 100644 --- a/component/componenttest/nop_factories.go +++ b/component/componenttest/nop_factories.go @@ -39,5 +39,9 @@ func NopFactories() (component.Factories, error) { return component.Factories{}, err } + if factories.Connectors, err = component.MakeConnectorFactoryMap(NewNopConnectorFactory()); err != nil { + return component.Factories{}, err + } + return factories, err } diff --git a/component/componenttest/nop_factories_test.go b/component/componenttest/nop_factories_test.go new file mode 100644 index 000000000000..15ea7df0550a --- /dev/null +++ b/component/componenttest/nop_factories_test.go @@ -0,0 +1,53 @@ +// 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 componenttest + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" +) + +func TestNopFactories(t *testing.T) { + nopFactories, err := NopFactories() + require.NoError(t, err) + + require.Equal(t, 1, len(nopFactories.Receivers)) + nopReceiverFactory, ok := nopFactories.Receivers["nop"] + require.True(t, ok) + require.Equal(t, component.Type("nop"), nopReceiverFactory.Type()) + + require.Equal(t, 1, len(nopFactories.Processors)) + nopProcessorFactory, ok := nopFactories.Processors["nop"] + require.True(t, ok) + require.Equal(t, component.Type("nop"), nopProcessorFactory.Type()) + + require.Equal(t, 1, len(nopFactories.Exporters)) + nopExporterFactory, ok := nopFactories.Exporters["nop"] + require.True(t, ok) + require.Equal(t, component.Type("nop"), nopExporterFactory.Type()) + + require.Equal(t, 1, len(nopFactories.Connectors)) + nopConnectorFactory, ok := nopFactories.Connectors["nop"] + require.True(t, ok) + require.Equal(t, component.Type("nop"), nopConnectorFactory.Type()) + + require.Equal(t, 1, len(nopFactories.Extensions)) + nopExtensionFactory, ok := nopFactories.Extensions["nop"] + require.True(t, ok) + require.Equal(t, component.Type("nop"), nopExtensionFactory.Type()) +} diff --git a/component/connector.go b/component/connector.go new file mode 100644 index 000000000000..288465862fe3 --- /dev/null +++ b/component/connector.go @@ -0,0 +1,516 @@ +// 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 component // import "go.opentelemetry.io/collector/component" + +import ( + "context" + "fmt" + + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +var ( + errDataTypesNotSupported = "connection from %s to %s is not supported" + ErrTracesToTraces = fmt.Errorf(errDataTypesNotSupported, DataTypeTraces, DataTypeTraces) + ErrTracesToMetrics = fmt.Errorf(errDataTypesNotSupported, DataTypeTraces, DataTypeMetrics) + ErrTracesToLogs = fmt.Errorf(errDataTypesNotSupported, DataTypeTraces, DataTypeLogs) + ErrMetricsToTraces = fmt.Errorf(errDataTypesNotSupported, DataTypeMetrics, DataTypeTraces) + ErrMetricsToMetrics = fmt.Errorf(errDataTypesNotSupported, DataTypeMetrics, DataTypeMetrics) + ErrMetricsToLogs = fmt.Errorf(errDataTypesNotSupported, DataTypeMetrics, DataTypeLogs) + ErrLogsToTraces = fmt.Errorf(errDataTypesNotSupported, DataTypeLogs, DataTypeTraces) + ErrLogsToMetrics = fmt.Errorf(errDataTypesNotSupported, DataTypeLogs, DataTypeMetrics) + ErrLogsToLogs = fmt.Errorf(errDataTypesNotSupported, DataTypeLogs, DataTypeLogs) +) + +// ConnectorConfig is the configuration of a component.Connector. Specific extensions must implement +// this interface and must embed ConnectorSettings struct or a struct that extends it. +type ConnectorConfig interface { + Config +} + +// UnmarshalConnectorConfig helper function to unmarshal a ConnectorConfig. +// It checks if the config implements confmap.Unmarshaler and uses that if available, +// otherwise uses Map.UnmarshalExact, erroring if a field is nonexistent. +func UnmarshalConnectorConfig(conf *confmap.Conf, cfg ConnectorConfig) error { + return unmarshal(conf, cfg) +} + +// Connector sends telemetry data from one pipeline to another. A connector +// is both an exporter and receiver, working together to connect pipelines. +// The purpose is to allow for differentiated processing of telemetry data. +// +// Connectors can be used to replicate or route data, merge data streams, +// derive signals from other signals, etc. +type Connector interface { + Component +} + +// A TracesToTracesConnector sends traces from one pipeline to another. +// Its purpose is to allow for differentiated processing of traces. +// TracesToTracesConnector feeds a consumer.Traces with data. +// +// For example traces could be collected in one pipeline and routed to another traces pipeline +// based on criteria such as attributes or other content of the trace. The second pipeline can +// then process and export the trace to the appropriate backend. +type TracesToTracesConnector interface { + Connector + ConsumeTracesToTraces(ctx context.Context, td ptrace.Traces) error +} + +// A TracesToMetricsConnector acts as an exporter from a traces pipeline and a receiver to a metrics pipeline. +// Its purpose is to derive metrics from a traces pipeline. +// TracesToMetricsConnector feeds a consumer.Metrics with data. +// +// For example traces could be summarized by a metrics connector that emits statistics describing the traces observed. +type TracesToMetricsConnector interface { + Connector + ConsumeTracesToMetrics(ctx context.Context, td ptrace.Traces) error +} + +// A TracesToLogsConnector acts as an exporter from a traces pipeline and a receiver to a logs pipeline. +// Its purpose is to derive logs from a traces pipeline. +// TracesToLogsConnector feeds a consumer.Logs with data. +// +// For example traces could be analyzed by a logs connector that emits events when particular criteria are met. +type TracesToLogsConnector interface { + Connector + ConsumeTracesToLogs(ctx context.Context, td ptrace.Traces) error +} + +// A MetricsToTracesConnector acts as an exporter from a metrics pipeline and a receiver to a traces pipeline. +// Its purpose is to derive traces from a metrics pipeline. +// MetricsToTracesConnector feeds a consumer.Traces with data. +// +// For example latency between related data points could be modeled and emitted as traces. +type MetricsToTracesConnector interface { + Connector + ConsumeMetricsToTraces(ctx context.Context, tm pmetric.Metrics) error +} + +// A MetricsToMetricsConnector sends metrics from one pipeline to another. +// Its purpose is to allow for differentiated processing of metrics. +// MetricsToMetricsConnector feeds a consumer.Metrics with data. +// +// For example metrics could be collected in one pipeline and routed to another metrics pipeline +// based on criteria such as attributes or other content of the metric. The second pipeline can +// then process and export the metric to the appropriate backend. +type MetricsToMetricsConnector interface { + Connector + ConsumeMetricsToMetrics(ctx context.Context, tm pmetric.Metrics) error +} + +// A MetricsToLogsConnector acts as an exporter from a metrics pipeline and a receiver to a logs pipeline. +// Its purpose is to derive logs from a metrics pipeline. +// MetricsToLogsConnector feeds a consumer.Logs with data. +// +// For example metrics could be analyzed by a logs connector that emits events when particular criteria are met. +type MetricsToLogsConnector interface { + Connector + ConsumeMetricsToLogs(ctx context.Context, tm pmetric.Metrics) error +} + +// A LogsToTracesConnector acts as an exporter from a logs pipeline and a receiver to a traces pipeline. +// Its purpose is to derive traces from a logs pipeline. +// LogsToTracesConnector feeds a consumer.Traces with data. +// +// For example structured logs containing span information could be consumed and emitted as traces. +type LogsToTracesConnector interface { + Connector + ConsumeLogsToTraces(ctx context.Context, tm plog.Logs) error +} + +// A LogsToMetricsConnector acts as an exporter from a logs pipeline and a receiver to a metrics pipeline. +// Its purpose is to derive metrics from a logs pipeline. +// LogsToMetricsConnector feeds a consumer.Metrics with data. +// +// For example metrics could be extracted from structured logs that contain numeric data. +type LogsToMetricsConnector interface { + Connector + ConsumeLogsToMetrics(ctx context.Context, tm plog.Logs) error +} + +// A LogsToLogsConnector sends logs from one pipeline to another. +// Its purpose is to allow for differentiated processing of logs. +// LogsToLogsConnector feeds a consumer.Logs with data. +// +// For example logs could be collected in one pipeline and routed to another logs pipeline +// based on criteria such as attributes or other content of the log. The second pipeline can +// then process and export the log to the appropriate backend. +type LogsToLogsConnector interface { + Connector + ConsumeLogsToLogs(ctx context.Context, tm plog.Logs) error +} + +// ConnectorCreateSettings configures Connector creators. +type ConnectorCreateSettings struct { + TelemetrySettings + + // BuildInfo can be used by components for informational purposes. + BuildInfo BuildInfo +} + +// ConnectorFactory is factory interface for connectors. +// +// This interface cannot be directly implemented. Implementations must +// use the NewConnectorFactory to implement it. +type ConnectorFactory interface { + Factory + + // CreateDefaultConfig creates the default configuration for the Connector. + // 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 Connector. + // The object returned by this method needs to pass the checks implemented by + // 'configtest.CheckConfigStruct'. It is recommended to have these checks in the + // tests of any implementation of the Factory interface. + CreateDefaultConfig() ConnectorConfig + + CreateTracesToTracesConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Traces) (TracesToTracesConnector, error) + CreateTracesToMetricsConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Metrics) (TracesToMetricsConnector, error) + CreateTracesToLogsConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Logs) (TracesToLogsConnector, error) + + CreateMetricsToTracesConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Traces) (MetricsToTracesConnector, error) + CreateMetricsToMetricsConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Metrics) (MetricsToMetricsConnector, error) + CreateMetricsToLogsConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Logs) (MetricsToLogsConnector, error) + + CreateLogsToTracesConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Traces) (LogsToTracesConnector, error) + CreateLogsToMetricsConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Metrics) (LogsToMetricsConnector, error) + CreateLogsToLogsConnector(ctx context.Context, set ConnectorCreateSettings, cfg ConnectorConfig, nextConsumer consumer.Logs) (LogsToLogsConnector, error) + + TracesToTracesConnectorStability() StabilityLevel + TracesToMetricsConnectorStability() StabilityLevel + TracesToLogsConnectorStability() StabilityLevel + + MetricsToTracesConnectorStability() StabilityLevel + MetricsToMetricsConnectorStability() StabilityLevel + MetricsToLogsConnectorStability() StabilityLevel + + LogsToTracesConnectorStability() StabilityLevel + LogsToMetricsConnectorStability() StabilityLevel + LogsToLogsConnectorStability() StabilityLevel +} + +// ConnectorFactoryOption apply changes to ConnectorOptions. +type ConnectorFactoryOption interface { + // applyConnectorFactoryOption applies the option. + applyConnectorFactoryOption(o *connectorFactory) +} + +var _ ConnectorFactoryOption = (*connectorFactoryOptionFunc)(nil) + +// connectorFactoryOptionFunc is an ConnectorFactoryOption created through a function. +type connectorFactoryOptionFunc func(*connectorFactory) + +func (f connectorFactoryOptionFunc) applyConnectorFactoryOption(o *connectorFactory) { + f(o) +} + +// ConnectorCreateDefaultConfigFunc is the equivalent of ConnectorFactory.CreateDefaultConfig(). +type ConnectorCreateDefaultConfigFunc func() ConnectorConfig + +// CreateDefaultConfig implements ConnectorFactory.CreateDefaultConfig(). +func (f ConnectorCreateDefaultConfigFunc) CreateDefaultConfig() ConnectorConfig { + return f() +} + +// CreateTracesToTracesConnectorFunc is the equivalent of ConnectorFactory.CreateTracesToTracesConnector(). +type CreateTracesToTracesConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Traces) (TracesToTracesConnector, error) + +// CreateTracesToTracesConnector implements ConnectorFactory.CreateTracesToTracesConnector(). +func (f CreateTracesToTracesConnectorFunc) CreateTracesToTracesConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Traces) (TracesToTracesConnector, error) { + if f == nil { + return nil, ErrTracesToTraces + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateTracesToMetricsConnectorFunc is the equivalent of ConnectorFactory.CreateTracesToMetricsConnector(). +type CreateTracesToMetricsConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Metrics) (TracesToMetricsConnector, error) + +// CreateTracesToMetricsConnector implements ConnectorFactory.CreateTracesToMetricsConnector(). +func (f CreateTracesToMetricsConnectorFunc) CreateTracesToMetricsConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Metrics, +) (TracesToMetricsConnector, error) { + if f == nil { + return nil, ErrTracesToMetrics + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateTracesToLogsConnectorFunc is the equivalent of ConnectorFactory.CreateTracesToLogsConnector(). +type CreateTracesToLogsConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Logs) (TracesToLogsConnector, error) + +// CreateTracesToLogsConnector implements ConnectorFactory.CreateTracesToLogsConnector(). +func (f CreateTracesToLogsConnectorFunc) CreateTracesToLogsConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Logs, +) (TracesToLogsConnector, error) { + if f == nil { + return nil, ErrTracesToLogs + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateMetricsToTracesConnectorFunc is the equivalent of ConnectorFactory.CreateMetricsToTracesConnector(). +type CreateMetricsToTracesConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Traces) (MetricsToTracesConnector, error) + +// CreateMetricsToTracesConnector implements ConnectorFactory.CreateMetricsToTracesConnector(). +func (f CreateMetricsToTracesConnectorFunc) CreateMetricsToTracesConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Traces, +) (MetricsToTracesConnector, error) { + if f == nil { + return nil, ErrMetricsToTraces + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateMetricsToMetricsConnectorFunc is the equivalent of ConnectorFactory.CreateMetricsToTracesConnector(). +type CreateMetricsToMetricsConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Metrics) (MetricsToMetricsConnector, error) + +// CreateMetricsToTracesConnector implements ConnectorFactory.CreateMetricsToTracesConnector(). +func (f CreateMetricsToMetricsConnectorFunc) CreateMetricsToMetricsConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Metrics, +) (MetricsToMetricsConnector, error) { + if f == nil { + return nil, ErrMetricsToMetrics + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateMetricsToLogsConnectorFunc is the equivalent of ConnectorFactory.CreateMetricsToLogsConnector(). +type CreateMetricsToLogsConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Logs) (MetricsToLogsConnector, error) + +// CreateMetricsToLogsConnector implements ConnectorFactory.CreateMetricsToLogsConnector(). +func (f CreateMetricsToLogsConnectorFunc) CreateMetricsToLogsConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Logs, +) (MetricsToLogsConnector, error) { + if f == nil { + return nil, ErrMetricsToLogs + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateLogsToTracesConnectorFunc is the equivalent of ConnectorFactory.CreateLogsToTracesConnector(). +type CreateLogsToTracesConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Traces) (LogsToTracesConnector, error) + +// CreateLogsToTracesConnector implements ConnectorFactory.CreateLogsToTracesConnector(). +func (f CreateLogsToTracesConnectorFunc) CreateLogsToTracesConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Traces, +) (LogsToTracesConnector, error) { + if f == nil { + return nil, ErrLogsToTraces + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateLogsToMetricssConnectorFunc is the equivalent of ConnectorFactory.CreateLogsToMetricsConnector(). +type CreateLogsToMetricsConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Metrics) (LogsToMetricsConnector, error) + +// CreateLogsToMetricsConnector implements ConnectorFactory.CreateLogsToMetricsConnector(). +func (f CreateLogsToMetricsConnectorFunc) CreateLogsToMetricsConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Metrics, +) (LogsToMetricsConnector, error) { + if f == nil { + return nil, ErrLogsToMetrics + } + return f(ctx, set, cfg, nextConsumer) +} + +// CreateLogsToLogsConnectorFunc is the equivalent of ConnectorFactory.CreateLogsToLogsConnector(). +type CreateLogsToLogsConnectorFunc func(context.Context, ConnectorCreateSettings, ConnectorConfig, consumer.Logs) (LogsToLogsConnector, error) + +// CreateLogsToLogsConnector implements ConnectorFactory.CreateLogsToLogsConnector(). +func (f CreateLogsToLogsConnectorFunc) CreateLogsToLogsConnector( + ctx context.Context, + set ConnectorCreateSettings, + cfg ConnectorConfig, + nextConsumer consumer.Logs, +) (LogsToLogsConnector, error) { + if f == nil { + return nil, ErrLogsToLogs + } + return f(ctx, set, cfg, nextConsumer) +} + +type connectorFactory struct { + baseFactory + ConnectorCreateDefaultConfigFunc + + CreateTracesToTracesConnectorFunc + CreateTracesToMetricsConnectorFunc + CreateTracesToLogsConnectorFunc + + CreateMetricsToTracesConnectorFunc + CreateMetricsToMetricsConnectorFunc + CreateMetricsToLogsConnectorFunc + + CreateLogsToTracesConnectorFunc + CreateLogsToMetricsConnectorFunc + CreateLogsToLogsConnectorFunc + + tracesToTracesStabilityLevel StabilityLevel + tracesToMetricsStabilityLevel StabilityLevel + tracesToLogsStabilityLevel StabilityLevel + + metricsToTracesStabilityLevel StabilityLevel + metricsToMetricsStabilityLevel StabilityLevel + metricsToLogsStabilityLevel StabilityLevel + + logsToTracesStabilityLevel StabilityLevel + logsToMetricsStabilityLevel StabilityLevel + logsToLogsStabilityLevel StabilityLevel +} + +// WithTracesToTracesConnector overrides the default "error not supported" implementation for WithTracesToTracesConnector and the default "undefined" stability level. +func WithTracesToTracesConnector(createTracesToTracesConnector CreateTracesToTracesConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.tracesToTracesStabilityLevel = sl + o.CreateTracesToTracesConnectorFunc = createTracesToTracesConnector + }) +} + +// WithTracesToMetricsConnector overrides the default "error not supported" implementation for WithTracesToMetricsConnector and the default "undefined" stability level. +func WithTracesToMetricsConnector(createTracesToMetricsConnector CreateTracesToMetricsConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.tracesToMetricsStabilityLevel = sl + o.CreateTracesToMetricsConnectorFunc = createTracesToMetricsConnector + }) +} + +// WithTracesToLogsConnector overrides the default "error not supported" implementation for WithTracesToLogsConnector and the default "undefined" stability level. +func WithTracesToLogsConnector(createTracesToLogsConnector CreateTracesToLogsConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.tracesToLogsStabilityLevel = sl + o.CreateTracesToLogsConnectorFunc = createTracesToLogsConnector + }) +} + +// WithMetricsToTracesConnector overrides the default "error not supported" implementation for WithMetricsToTracesConnector and the default "undefined" stability level. +func WithMetricsToTracesConnector(createMetricsToTracesConnector CreateMetricsToTracesConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.metricsToTracesStabilityLevel = sl + o.CreateMetricsToTracesConnectorFunc = createMetricsToTracesConnector + }) +} + +// WithMetricsToMetricsConnector overrides the default "error not supported" implementation for WithMetricsToMetricsConnector and the default "undefined" stability level. +func WithMetricsToMetricsConnector(createMetricsToMetricsConnector CreateMetricsToMetricsConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.metricsToMetricsStabilityLevel = sl + o.CreateMetricsToMetricsConnectorFunc = createMetricsToMetricsConnector + }) +} + +// WithMetricsToLogsConnector overrides the default "error not supported" implementation for WithMetricsToLogsConnector and the default "undefined" stability level. +func WithMetricsToLogsConnector(createMetricsToLogsConnector CreateMetricsToLogsConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.metricsToLogsStabilityLevel = sl + o.CreateMetricsToLogsConnectorFunc = createMetricsToLogsConnector + }) +} + +// WithLogsToTracesConnector overrides the default "error not supported" implementation for WithLogsToTracesConnector and the default "undefined" stability level. +func WithLogsToTracesConnector(createLogsToTracesConnector CreateLogsToTracesConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.logsToTracesStabilityLevel = sl + o.CreateLogsToTracesConnectorFunc = createLogsToTracesConnector + }) +} + +// WithLogsToMetricsConnector overrides the default "error not supported" implementation for WithLogsToMetricsConnector and the default "undefined" stability level. +func WithLogsToMetricsConnector(createLogsToMetricsConnector CreateLogsToMetricsConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.logsToMetricsStabilityLevel = sl + o.CreateLogsToMetricsConnectorFunc = createLogsToMetricsConnector + }) +} + +// WithLogsToLogsConnector overrides the default "error not supported" implementation for WithLogsToLogsConnector and the default "undefined" stability level. +func WithLogsToLogsConnector(createLogsToLogsConnector CreateLogsToLogsConnectorFunc, sl StabilityLevel) ConnectorFactoryOption { + return connectorFactoryOptionFunc(func(o *connectorFactory) { + o.logsToLogsStabilityLevel = sl + o.CreateLogsToLogsConnectorFunc = createLogsToLogsConnector + }) +} + +func (p connectorFactory) TracesToTracesConnectorStability() StabilityLevel { + return p.tracesToTracesStabilityLevel +} +func (p connectorFactory) TracesToMetricsConnectorStability() StabilityLevel { + return p.tracesToMetricsStabilityLevel +} +func (p connectorFactory) TracesToLogsConnectorStability() StabilityLevel { + return p.tracesToLogsStabilityLevel +} + +func (p connectorFactory) MetricsToTracesConnectorStability() StabilityLevel { + return p.metricsToTracesStabilityLevel +} +func (p connectorFactory) MetricsToMetricsConnectorStability() StabilityLevel { + return p.metricsToMetricsStabilityLevel +} +func (p connectorFactory) MetricsToLogsConnectorStability() StabilityLevel { + return p.metricsToLogsStabilityLevel +} + +func (p connectorFactory) LogsToTracesConnectorStability() StabilityLevel { + return p.logsToTracesStabilityLevel +} +func (p connectorFactory) LogsToMetricsConnectorStability() StabilityLevel { + return p.logsToMetricsStabilityLevel +} +func (p connectorFactory) LogsToLogsConnectorStability() StabilityLevel { + return p.logsToLogsStabilityLevel +} + +// NewConnectorFactory returns a ConnectorFactory. +func NewConnectorFactory(cfgType Type, createDefaultConfig ConnectorCreateDefaultConfigFunc, options ...ConnectorFactoryOption) ConnectorFactory { + f := &connectorFactory{ + baseFactory: baseFactory{cfgType: cfgType}, + ConnectorCreateDefaultConfigFunc: createDefaultConfig, + } + for _, opt := range options { + opt.applyConnectorFactoryOption(f) + } + return f +} diff --git a/component/connector_test.go b/component/connector_test.go new file mode 100644 index 000000000000..dc35a93d7a2e --- /dev/null +++ b/component/connector_test.go @@ -0,0 +1,225 @@ +// 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. + +// TODO: Move tests back to component package after config.*Settings are removed. + +package component_test // import "go.opentelemetry.io/collector/component" + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/consumer" +) + +func TestNewConnectorFactory_NoOptions(t *testing.T) { + const typeStr = "test" + defaultCfg := config.NewConnectorSettings(component.NewID(typeStr)) + factory := component.NewConnectorFactory( + typeStr, + func() component.ConnectorConfig { return &defaultCfg }) + assert.EqualValues(t, typeStr, factory.Type()) + assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) + + _, err := factory.CreateTracesToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrTracesToTraces) + _, err = factory.CreateTracesToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrTracesToMetrics) + _, err = factory.CreateTracesToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrTracesToLogs) + + _, err = factory.CreateMetricsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrMetricsToTraces) + _, err = factory.CreateMetricsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrMetricsToMetrics) + _, err = factory.CreateMetricsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrMetricsToLogs) + + _, err = factory.CreateLogsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrLogsToTraces) + _, err = factory.CreateLogsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrLogsToMetrics) + _, err = factory.CreateLogsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrLogsToLogs) +} + +func TestNewConnectorFactory_WithSameTypes(t *testing.T) { + const typeStr = "test" + defaultCfg := config.NewConnectorSettings(component.NewID(typeStr)) + factory := component.NewConnectorFactory( + typeStr, + func() component.ConnectorConfig { return &defaultCfg }, + component.WithTracesToTracesConnector(createTracesToTracesConnector, component.StabilityLevelAlpha), + component.WithMetricsToMetricsConnector(createMetricsToMetricsConnector, component.StabilityLevelBeta), + component.WithLogsToLogsConnector(createLogsToLogsConnector, component.StabilityLevelUnmaintained)) + assert.EqualValues(t, typeStr, factory.Type()) + assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) + + assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToTracesConnectorStability()) + _, err := factory.CreateTracesToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToMetricsConnectorStability()) + _, err = factory.CreateMetricsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToLogsConnectorStability()) + _, err = factory.CreateLogsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + _, err = factory.CreateTracesToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrTracesToMetrics) + _, err = factory.CreateTracesToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrTracesToLogs) + + _, err = factory.CreateMetricsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrMetricsToTraces) + _, err = factory.CreateMetricsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrMetricsToLogs) + + _, err = factory.CreateLogsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrLogsToTraces) + _, err = factory.CreateLogsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrLogsToMetrics) +} + +func TestNewConnectorFactory_WithTranslateTypes(t *testing.T) { + const typeStr = "test" + defaultCfg := config.NewConnectorSettings(component.NewID(typeStr)) + factory := component.NewConnectorFactory( + typeStr, + func() component.ConnectorConfig { return &defaultCfg }, + component.WithTracesToMetricsConnector(createTracesToMetricsConnector, component.StabilityLevelDevelopment), + component.WithTracesToLogsConnector(createTracesToLogsConnector, component.StabilityLevelAlpha), + component.WithMetricsToTracesConnector(createMetricsToTracesConnector, component.StabilityLevelBeta), + component.WithMetricsToLogsConnector(createMetricsToLogsConnector, component.StabilityLevelStable), + component.WithLogsToTracesConnector(createLogsToTracesConnector, component.StabilityLevelDeprecated), + component.WithLogsToMetricsConnector(createLogsToMetricsConnector, component.StabilityLevelUnmaintained)) + assert.EqualValues(t, typeStr, factory.Type()) + assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) + + _, err := factory.CreateTracesToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrTracesToTraces) + _, err = factory.CreateMetricsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrMetricsToMetrics) + _, err = factory.CreateLogsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.Equal(t, err, component.ErrLogsToLogs) + + assert.Equal(t, component.StabilityLevelDevelopment, factory.TracesToMetricsConnectorStability()) + _, err = factory.CreateTracesToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToLogsConnectorStability()) + _, err = factory.CreateTracesToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToTracesConnectorStability()) + _, err = factory.CreateMetricsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelStable, factory.MetricsToLogsConnectorStability()) + _, err = factory.CreateMetricsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelDeprecated, factory.LogsToTracesConnectorStability()) + _, err = factory.CreateLogsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToMetricsConnectorStability()) + _, err = factory.CreateLogsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) +} + +func TestNewConnectorFactory_WithAllTypes(t *testing.T) { + const typeStr = "test" + defaultCfg := config.NewConnectorSettings(component.NewID(typeStr)) + factory := component.NewConnectorFactory( + typeStr, + func() component.ConnectorConfig { return &defaultCfg }, + component.WithTracesToTracesConnector(createTracesToTracesConnector, component.StabilityLevelAlpha), + component.WithTracesToMetricsConnector(createTracesToMetricsConnector, component.StabilityLevelDevelopment), + component.WithTracesToLogsConnector(createTracesToLogsConnector, component.StabilityLevelAlpha), + component.WithMetricsToTracesConnector(createMetricsToTracesConnector, component.StabilityLevelBeta), + component.WithMetricsToMetricsConnector(createMetricsToMetricsConnector, component.StabilityLevelBeta), + component.WithMetricsToLogsConnector(createMetricsToLogsConnector, component.StabilityLevelStable), + component.WithLogsToTracesConnector(createLogsToTracesConnector, component.StabilityLevelDeprecated), + component.WithLogsToMetricsConnector(createLogsToMetricsConnector, component.StabilityLevelUnmaintained), + component.WithLogsToLogsConnector(createLogsToLogsConnector, component.StabilityLevelUnmaintained)) + assert.EqualValues(t, typeStr, factory.Type()) + assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig()) + + assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToTracesConnectorStability()) + _, err := factory.CreateTracesToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + assert.Equal(t, component.StabilityLevelDevelopment, factory.TracesToMetricsConnectorStability()) + _, err = factory.CreateTracesToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + assert.Equal(t, component.StabilityLevelAlpha, factory.TracesToLogsConnectorStability()) + _, err = factory.CreateTracesToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToTracesConnectorStability()) + _, err = factory.CreateMetricsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + assert.Equal(t, component.StabilityLevelBeta, factory.MetricsToMetricsConnectorStability()) + _, err = factory.CreateMetricsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + assert.Equal(t, component.StabilityLevelStable, factory.MetricsToLogsConnectorStability()) + _, err = factory.CreateMetricsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + + assert.Equal(t, component.StabilityLevelDeprecated, factory.LogsToTracesConnectorStability()) + _, err = factory.CreateLogsToTracesConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToMetricsConnectorStability()) + _, err = factory.CreateLogsToMetricsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) + assert.Equal(t, component.StabilityLevelUnmaintained, factory.LogsToLogsConnectorStability()) + _, err = factory.CreateLogsToLogsConnector(context.Background(), component.ConnectorCreateSettings{}, &defaultCfg, nil) + assert.NoError(t, err) +} + +func createTracesToTracesConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.TracesToTracesConnector, error) { + return nil, nil +} +func createTracesToMetricsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.TracesToMetricsConnector, error) { + return nil, nil +} +func createTracesToLogsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.TracesToLogsConnector, error) { + return nil, nil +} + +func createMetricsToTracesConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.MetricsToTracesConnector, error) { + return nil, nil +} +func createMetricsToMetricsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.MetricsToMetricsConnector, error) { + return nil, nil +} +func createMetricsToLogsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.MetricsToLogsConnector, error) { + return nil, nil +} + +func createLogsToTracesConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.LogsToTracesConnector, error) { + return nil, nil +} +func createLogsToMetricsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.LogsToMetricsConnector, error) { + return nil, nil +} +func createLogsToLogsConnector(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.LogsToLogsConnector, error) { + return nil, nil +} diff --git a/component/factories.go b/component/factories.go index e666afaf73ca..334c89d52c03 100644 --- a/component/factories.go +++ b/component/factories.go @@ -32,6 +32,9 @@ type Factories struct { // Extensions maps extension type names in the config to the respective factory. Extensions map[Type]ExtensionFactory + + // Connectors maps connector type names in the config to the respective factory. + Connectors map[Type]ConnectorFactory } // MakeReceiverFactoryMap takes a list of receiver factories and returns a map @@ -76,6 +79,20 @@ func MakeExporterFactoryMap(factories ...ExporterFactory) (map[Type]ExporterFact return fMap, nil } +// // MakeConnectorFactoryMap takes a list of connector factories and returns a map +// // with factory type as keys. It returns a non-nil error when more than one factories +// // have the same type. +func MakeConnectorFactoryMap(factories ...ConnectorFactory) (map[Type]ConnectorFactory, error) { + fMap := map[Type]ConnectorFactory{} + for _, f := range factories { + if _, ok := fMap[f.Type()]; ok { + return fMap, fmt.Errorf("duplicate connector factory %q", f.Type()) + } + fMap[f.Type()] = f + } + return fMap, nil +} + // MakeExtensionFactoryMap takes a list of extension factories and returns a map // with factory type as keys. It returns a non-nil error when more than one factories // have the same type. diff --git a/component/factories_test.go b/component/factories_test.go index 7b1972cd34b6..480d295ce7c0 100644 --- a/component/factories_test.go +++ b/component/factories_test.go @@ -170,3 +170,41 @@ func TestMakeExporterFactoryMap(t *testing.T) { }) } } + +func TestMakeConnectorFactoryMap(t *testing.T) { + type testCase struct { + name string + in []ConnectorFactory + out map[Type]ConnectorFactory + } + + p1 := NewConnectorFactory("p1", nil) + p2 := NewConnectorFactory("p2", nil) + testCases := []testCase{ + { + name: "different names", + in: []ConnectorFactory{p1, p2}, + out: map[Type]ConnectorFactory{ + p1.Type(): p1, + p2.Type(): p2, + }, + }, + { + name: "same name", + in: []ConnectorFactory{p1, p2, NewConnectorFactory("p1", nil)}, + }, + } + + for i := range testCases { + tt := testCases[i] + t.Run(tt.name, func(t *testing.T) { + out, err := MakeConnectorFactoryMap(tt.in...) + if tt.out == nil { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.out, out) + }) + } +} diff --git a/config/connector.go b/config/connector.go new file mode 100644 index 000000000000..7a362cadef0f --- /dev/null +++ b/config/connector.go @@ -0,0 +1,51 @@ +// 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 config // import "go.opentelemetry.io/collector/config" +import ( + "go.opentelemetry.io/collector/component" +) + +// ConnectorSettings defines common settings for a component.Connector configuration. +// Specific exporters can embed this struct and extend it with more fields if needed. +// +// It is highly recommended to "override" the Validate() function. +// +// When embedded in the exporter config, it must be with `mapstructure:",squash"` tag. +type ConnectorSettings struct { + id component.ID `mapstructure:"-"` + component.ConnectorConfig +} + +// NewConnectorSettings return a new ConnectorSettings with the given ComponentID. +func NewConnectorSettings(id component.ID) ConnectorSettings { + return ConnectorSettings{id: id} +} + +var _ component.ConnectorConfig = (*ConnectorSettings)(nil) + +// ID returns the receiver ComponentID. +func (cs *ConnectorSettings) ID() component.ID { + return cs.id +} + +// SetIDName sets the receiver name. +func (cs *ConnectorSettings) SetIDName(idName string) { + cs.id = component.NewIDWithName(cs.id.Type(), idName) +} + +// Validate validates the configuration and returns an error if invalid. +func (cs *ConnectorSettings) Validate() error { + return nil +} diff --git a/connector/countconnector/Makefile b/connector/countconnector/Makefile new file mode 100644 index 000000000000..ded7a36092dc --- /dev/null +++ b/connector/countconnector/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/connector/countconnector/README.md b/connector/countconnector/README.md new file mode 100644 index 000000000000..bc68adfe6d43 --- /dev/null +++ b/connector/countconnector/README.md @@ -0,0 +1,18 @@ +# Count Connector + +The `countconnector` can be used to count signals. + +## Supported connection types + +Connectors are always used in two or more pipelines. Therefore, support and stability +are defined per _pair of signal types_. The pipeline in which a connector is used as +an exporter is referred to below as the "Exporter pipeline". Likewise, the pipeline in +which the connector is used as a receiver is referred to below as the "Receiver pipeline". + +| Exporter pipeline | Receiver pipeline | Stability | +| ----------------- | ----------------- | ----------------- | +| traces | metrics | [in development] | +| metrics | metrics | [in development] | +| logs | metrics | [in development] | + +[in development]:https://github.com/open-telemetry/opentelemetry-collector#in-development diff --git a/connector/countconnector/count.go b/connector/countconnector/count.go new file mode 100644 index 000000000000..5371f00dfa1e --- /dev/null +++ b/connector/countconnector/count.go @@ -0,0 +1,205 @@ +// 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 countconnector // import "go.opentelemetry.io/collector/connector/countconnector" + +import ( + "context" + "fmt" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/internal/sharedcomponent" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +const ( + typeStr = "count" + scopeName = "otelcol/countconnector" +) + +type Config struct { + config.ConnectorSettings `mapstructure:",squash"` +} + +var _ component.ConnectorConfig = (*Config)(nil) + +// NewFactory returns a ConnectorFactory. +func NewFactory() component.ConnectorFactory { + return component.NewConnectorFactory( + typeStr, + createDefaultConfig, + component.WithTracesToMetricsConnector(createTracesToMetricsConnector, component.StabilityLevelDevelopment), + component.WithMetricsToMetricsConnector(createMetricsToMetricsConnector, component.StabilityLevelDevelopment), + component.WithLogsToMetricsConnector(createLogsToMetricsConnector, component.StabilityLevelDevelopment), + ) +} + +// createDefaultConfig creates the default configuration. +func createDefaultConfig() component.ConnectorConfig { + return &Config{} +} + +// createTracesToMetricsConnector creates a traces to metrics connector based on provided config. +func createTracesToMetricsConnector( + _ context.Context, + set component.ConnectorCreateSettings, + cfg component.ConnectorConfig, + nextConsumer consumer.Metrics, +) (component.TracesToMetricsConnector, error) { + comp := connectors.GetOrAdd(cfg.ID(), func() component.Component { + return newCountConnector(cfg.(*Config), set) + }) + + conn := comp.Unwrap().(*countConnector) + conn.metricsConsumer = nextConsumer + return conn, nil +} + +// createMetricsConnector creates a metrics connector based on provided config. +func createMetricsToMetricsConnector( + _ context.Context, + set component.ConnectorCreateSettings, + cfg component.ConnectorConfig, + nextConsumer consumer.Metrics, +) (component.MetricsToMetricsConnector, error) { + comp := connectors.GetOrAdd(cfg.ID(), func() component.Component { + return newCountConnector(cfg.(*Config), set) + }) + + conn := comp.Unwrap().(*countConnector) + conn.metricsConsumer = nextConsumer + return conn, nil +} + +// createLogsToMetricsConnector creates a logs to metrics connector based on provided config. +func createLogsToMetricsConnector( + _ context.Context, + set component.ConnectorCreateSettings, + cfg component.ConnectorConfig, + nextConsumer consumer.Metrics, +) (component.LogsToMetricsConnector, error) { + comp := connectors.GetOrAdd(cfg.ID(), func() component.Component { + return newCountConnector(cfg.(*Config), set) + }) + + conn := comp.Unwrap().(*countConnector) + conn.metricsConsumer = nextConsumer + return conn, nil +} + +// This is the map of already created count connectors for particular configurations. +// We maintain this map because the Factory is asked trace, metric, and log receivers +// separately but they must not create separate objects. When the connector is shutdown +// it should be removed from this map so the same configuration can be recreated successfully. +var connectors = sharedcomponent.NewSharedComponents() + +// otlpReceiver is the type that exposes Trace and Metrics reception. +type countConnector struct { + cfg *Config + settings component.ConnectorCreateSettings + metricsConsumer consumer.Metrics +} + +func newCountConnector(cfg *Config, settings component.ConnectorCreateSettings) *countConnector { + return &countConnector{ + cfg: cfg, + settings: settings, + } +} + +func (c *countConnector) Capabilities() consumer.Capabilities { + return consumer.Capabilities{MutatesData: false} +} + +func (c *countConnector) Start(ctx context.Context, host component.Host) error { + return nil +} + +func (c *countConnector) Shutdown(ctx context.Context) error { + return nil +} + +func (c *countConnector) ConsumeTracesToMetrics(ctx context.Context, td ptrace.Traces) error { + countMetrics := pmetric.NewMetrics() + for i := 0; i < td.ResourceSpans().Len(); i++ { + resourceSpan := td.ResourceSpans().At(i) + countResource := countMetrics.ResourceMetrics().AppendEmpty() + resourceSpan.Resource().Attributes().CopyTo(countResource.Resource().Attributes()) + + countScope := countResource.ScopeMetrics().AppendEmpty() + countScope.Scope().SetName(scopeName) + + count := 0 + for j := 0; j < resourceSpan.ScopeSpans().Len(); j++ { + count += resourceSpan.ScopeSpans().At(j).Spans().Len() + } + setCountMetric(countScope.Metrics().AppendEmpty(), "span", count) + } + return c.metricsConsumer.ConsumeMetrics(ctx, countMetrics) +} + +func (c *countConnector) ConsumeMetricsToMetrics(ctx context.Context, md pmetric.Metrics) error { + countMetrics := pmetric.NewMetrics() + for i := 0; i < md.ResourceMetrics().Len(); i++ { + resourceMetric := md.ResourceMetrics().At(i) + countResource := countMetrics.ResourceMetrics().AppendEmpty() + resourceMetric.Resource().Attributes().CopyTo(countResource.Resource().Attributes()) + + countScope := countResource.ScopeMetrics().AppendEmpty() + countScope.Scope().SetName(scopeName) + + count := 0 + for j := 0; j < resourceMetric.ScopeMetrics().Len(); j++ { + count += resourceMetric.ScopeMetrics().At(j).Metrics().Len() + } + setCountMetric(countScope.Metrics().AppendEmpty(), "metric", count) + } + return c.metricsConsumer.ConsumeMetrics(ctx, countMetrics) +} + +func (c *countConnector) ConsumeLogsToMetrics(ctx context.Context, ld plog.Logs) error { + countMetrics := pmetric.NewMetrics() + for i := 0; i < ld.ResourceLogs().Len(); i++ { + resourceLog := ld.ResourceLogs().At(i) + countResource := countMetrics.ResourceMetrics().AppendEmpty() + resourceLog.Resource().Attributes().CopyTo(countResource.Resource().Attributes()) + + countScope := countResource.ScopeMetrics().AppendEmpty() + countScope.Scope().SetName(scopeName) + + count := 0 + for j := 0; j < resourceLog.ScopeLogs().Len(); j++ { + count += resourceLog.ScopeLogs().At(j).LogRecords().Len() + } + setCountMetric(countScope.Metrics().AppendEmpty(), "log", count) + } + return c.metricsConsumer.ConsumeMetrics(ctx, countMetrics) +} + +func setCountMetric(countMetric pmetric.Metric, signalType string, count int) { + countMetric.SetName(fmt.Sprintf("%s.count", signalType)) + countMetric.SetDescription(fmt.Sprintf("The number of %s observed.", signalType)) + sum := countMetric.SetEmptySum() + sum.SetIsMonotonic(true) + sum.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) + dp := sum.DataPoints().AppendEmpty() + dp.SetIntValue(int64(count)) + dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) +} diff --git a/connector/countconnector/count_test.go b/connector/countconnector/count_test.go new file mode 100644 index 000000000000..56f9597dfedb --- /dev/null +++ b/connector/countconnector/count_test.go @@ -0,0 +1,127 @@ +// 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 countconnector + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +func TestCount(t *testing.T) { + f := NewFactory() + cfg := f.CreateDefaultConfig() + assert.Equal(t, &Config{}, cfg) + + ctx := context.Background() + set := componenttest.NewNopConnectorCreateSettings() + host := componenttest.NewNopHost() + + countsSink := new(consumertest.MetricsSink) + + spansCounter, err := f.CreateTracesToMetricsConnector(ctx, set, cfg, countsSink) + assert.NoError(t, err) + assert.NotNil(t, spansCounter) + + metricsCounter, err := f.CreateMetricsToMetricsConnector(ctx, set, cfg, countsSink) + assert.NoError(t, err) + assert.NotNil(t, metricsCounter) + + logsCounter, err := f.CreateLogsToMetricsConnector(ctx, set, cfg, countsSink) + assert.NoError(t, err) + assert.NotNil(t, logsCounter) + + assert.NoError(t, spansCounter.Start(ctx, host)) + assert.NoError(t, metricsCounter.Start(ctx, host)) + assert.NoError(t, logsCounter.Start(ctx, host)) + + spanCounts := []int64{4, 8, 2, 25, 100} + assert.NoError(t, spansCounter.ConsumeTracesToMetrics(ctx, dummySpans(spanCounts))) + + metricCounts := []int64{2, 5, 1, 10} + assert.NoError(t, metricsCounter.ConsumeMetricsToMetrics(ctx, dummyMetrics(metricCounts))) + + logCounts := []int64{1, 1, 2, 3, 5, 8, 13} + assert.NoError(t, logsCounter.ConsumeLogsToMetrics(ctx, dummyLogs(logCounts))) + + assert.NoError(t, spansCounter.Shutdown(ctx)) + assert.NoError(t, metricsCounter.Shutdown(ctx)) + assert.NoError(t, logsCounter.Shutdown(ctx)) + + assert.Equal(t, 3, len(countsSink.AllMetrics())) + assertEqualCounts(t, spanCounts, "span.count", countsSink.AllMetrics()[0].ResourceMetrics()) + assertEqualCounts(t, metricCounts, "metric.count", countsSink.AllMetrics()[1].ResourceMetrics()) + assertEqualCounts(t, logCounts, "log.count", countsSink.AllMetrics()[2].ResourceMetrics()) + +} + +func assertEqualCounts(t *testing.T, expectedCounts []int64, expectedName string, resources pmetric.ResourceMetricsSlice) { + for i, c := range expectedCounts { + scopeMetric := resources.At(i).ScopeMetrics().At(0) + assert.Equal(t, scopeName, scopeMetric.Scope().Name()) + assert.Equal(t, 1, scopeMetric.Metrics().Len()) + countMetric := scopeMetric.Metrics().At(0) + assert.Equal(t, expectedName, countMetric.Name()) + assert.Equal(t, 1, countMetric.Sum().DataPoints().Len()) + countDataPoint := countMetric.Sum().DataPoints().At(0) + assert.Equal(t, c, countDataPoint.IntValue()) + } +} + +func dummySpans(spanCounts []int64) ptrace.Traces { + result := ptrace.NewTraces() + resources := result.ResourceSpans() + for i := 0; i < len(spanCounts); i++ { + resource := resources.AppendEmpty() + spans := resource.ScopeSpans().AppendEmpty().Spans() + for j := int64(0); j < spanCounts[i]; j++ { + spans.AppendEmpty().SetName(fmt.Sprintf("some.span.%d", j)) + } + } + return result +} + +func dummyMetrics(metricCounts []int64) pmetric.Metrics { + result := pmetric.NewMetrics() + resources := result.ResourceMetrics() + for i := 0; i < len(metricCounts); i++ { + resource := resources.AppendEmpty() + metrics := resource.ScopeMetrics().AppendEmpty().Metrics() + for j := int64(0); j < metricCounts[i]; j++ { + metrics.AppendEmpty().SetName(fmt.Sprintf("some.metric.%d", j)) + } + } + return result +} + +func dummyLogs(logCounts []int64) plog.Logs { + result := plog.NewLogs() + resources := result.ResourceLogs() + for i := 0; i < len(logCounts); i++ { + resource := resources.AppendEmpty() + logs := resource.ScopeLogs().AppendEmpty().LogRecords() + for j := int64(0); j < logCounts[i]; j++ { + logs.AppendEmpty().Body().SetStr(fmt.Sprintf("some log %d", j)) + } + } + return result +} diff --git a/connector/countconnector/doc.go b/connector/countconnector/doc.go new file mode 100644 index 000000000000..bfbe4b53df97 --- /dev/null +++ b/connector/countconnector/doc.go @@ -0,0 +1,16 @@ +// 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 countconnector counts signals other pipelines. +package countconnector // import "go.opentelemetry.io/collector/connector/countconnector" diff --git a/connector/countconnector/go.mod b/connector/countconnector/go.mod new file mode 100644 index 000000000000..da4ed724980e --- /dev/null +++ b/connector/countconnector/go.mod @@ -0,0 +1,51 @@ +module go.opentelemetry.io/collector/connector/countconnector + +go 1.19 + +require ( + github.com/stretchr/testify v1.8.1 + go.opentelemetry.io/collector v0.65.0 + go.opentelemetry.io/collector/component v0.65.0 + go.opentelemetry.io/collector/consumer v0.65.0 + go.opentelemetry.io/collector/pdata v0.65.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/knadh/koanf v1.4.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/featuregate v0.65.0 // indirect + go.opentelemetry.io/otel v1.11.1 // indirect + go.opentelemetry.io/otel/metric v0.33.0 // indirect + go.opentelemetry.io/otel/trace v1.11.1 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.23.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/collector => ../../ + +replace go.opentelemetry.io/collector/component => ../../component + +replace go.opentelemetry.io/collector/pdata => ../../pdata + +replace go.opentelemetry.io/collector/extension/zpagesextension => ../../extension/zpagesextension + +replace go.opentelemetry.io/collector/semconv => ../../semconv + +replace go.opentelemetry.io/collector/processor/batchprocessor => ../../processor/batchprocessor diff --git a/connector/countconnector/go.sum b/connector/countconnector/go.sum new file mode 100644 index 000000000000..2a3041265418 --- /dev/null +++ b/connector/countconnector/go.sum @@ -0,0 +1,441 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= +github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= +github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knadh/koanf v1.4.4 h1:d2jY5nCCeoaiqvEKSBW9rEc93EfNy/XWgWsSB3j7JEA= +github.com/knadh/koanf v1.4.4/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= +go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= +go.opentelemetry.io/collector/component v0.65.0 h1:6r71RfuzXyqyVeuU2+YBBS5xWfMQJ1INSKCTtGjBQLc= +go.opentelemetry.io/collector/component v0.65.0/go.mod h1:0c84EqXUhvYe6KW7hJfh76tiI/5yjWCH2amwyQ06XLM= +go.opentelemetry.io/collector/consumer v0.65.0 h1:9pjl1zV8nQ2QtQLuCcu0X7i9k+qXlTsE3YS9IaIQqHY= +go.opentelemetry.io/collector/consumer v0.65.0/go.mod h1:WtoRZa5SnxQO1ZEQdVxYpFcXCmq62rakv0oUSlPO0NQ= +go.opentelemetry.io/collector/featuregate v0.65.0 h1:Z0fLrzFgbHdkS3msfEXYgXN9273sXF4oRJ75IItlcgk= +go.opentelemetry.io/collector/featuregate v0.65.0/go.mod h1:tewuFKJYalWBU0bmNKg++MC1ipINXUr6szYzOw2p1GI= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= +go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/connector/nopconnector/Makefile b/connector/nopconnector/Makefile new file mode 100644 index 000000000000..ded7a36092dc --- /dev/null +++ b/connector/nopconnector/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/connector/nopconnector/README.md b/connector/nopconnector/README.md new file mode 100644 index 000000000000..5bcae8135f2f --- /dev/null +++ b/connector/nopconnector/README.md @@ -0,0 +1,21 @@ +# Nop Connector + +The `nopconnector` can be used to chain pipelines of the same type together. +For example, it can replicate a signal to multiple pipelines so that each pipeline +can process the signal independently in varying ways. Alternately, it can be used to +merge two pipelines together so they can be processed as one pipeline. + +## Supported connection types + +Connectors are always used in two or more pipelines. Therefore, support and stability +are defined per _pair of signal types_. The pipeline in which a connector is used as +an exporter is referred to below as the "Exporter pipeline". Likewise, the pipeline in +which the connector is used as a receiver is referred to below as the "Receiver pipeline". + +| Exporter pipeline | Receiver pipeline | Stability | +| ----------------- | ----------------- | ----------------- | +| traces | traces | [in development] | +| metrics | metrics | [in development] | +| logs | logs | [in development] | + +[in development]:https://github.com/open-telemetry/opentelemetry-collector#in-development diff --git a/connector/nopconnector/doc.go b/connector/nopconnector/doc.go new file mode 100644 index 000000000000..4b93f3d66336 --- /dev/null +++ b/connector/nopconnector/doc.go @@ -0,0 +1,16 @@ +// 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 nopconnector passes signals from one pipeline to another. +package nopconnector // import "go.opentelemetry.io/collector/connector/nopconnector" diff --git a/connector/nopconnector/go.mod b/connector/nopconnector/go.mod new file mode 100644 index 000000000000..cad8e53083ef --- /dev/null +++ b/connector/nopconnector/go.mod @@ -0,0 +1,51 @@ +module go.opentelemetry.io/collector/connector/nopconnector + +go 1.19 + +require ( + github.com/stretchr/testify v1.8.1 + go.opentelemetry.io/collector v0.65.0 + go.opentelemetry.io/collector/component v0.65.0 + go.opentelemetry.io/collector/consumer v0.65.0 + go.opentelemetry.io/collector/pdata v0.65.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/knadh/koanf v1.4.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/featuregate v0.65.0 // indirect + go.opentelemetry.io/otel v1.11.1 // indirect + go.opentelemetry.io/otel/metric v0.33.0 // indirect + go.opentelemetry.io/otel/trace v1.11.1 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.23.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/collector => ../../ + +replace go.opentelemetry.io/collector/component => ../../component + +replace go.opentelemetry.io/collector/pdata => ../../pdata + +replace go.opentelemetry.io/collector/semconv => ../../semconv + +replace go.opentelemetry.io/collector/processor/batchprocessor => ../../processor/batchprocessor + +replace go.opentelemetry.io/collector/extension/zpagesextension => ../../extension/zpagesextension diff --git a/connector/nopconnector/go.sum b/connector/nopconnector/go.sum new file mode 100644 index 000000000000..2a3041265418 --- /dev/null +++ b/connector/nopconnector/go.sum @@ -0,0 +1,441 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= +github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= +github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knadh/koanf v1.4.4 h1:d2jY5nCCeoaiqvEKSBW9rEc93EfNy/XWgWsSB3j7JEA= +github.com/knadh/koanf v1.4.4/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= +go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= +go.opentelemetry.io/collector/component v0.65.0 h1:6r71RfuzXyqyVeuU2+YBBS5xWfMQJ1INSKCTtGjBQLc= +go.opentelemetry.io/collector/component v0.65.0/go.mod h1:0c84EqXUhvYe6KW7hJfh76tiI/5yjWCH2amwyQ06XLM= +go.opentelemetry.io/collector/consumer v0.65.0 h1:9pjl1zV8nQ2QtQLuCcu0X7i9k+qXlTsE3YS9IaIQqHY= +go.opentelemetry.io/collector/consumer v0.65.0/go.mod h1:WtoRZa5SnxQO1ZEQdVxYpFcXCmq62rakv0oUSlPO0NQ= +go.opentelemetry.io/collector/featuregate v0.65.0 h1:Z0fLrzFgbHdkS3msfEXYgXN9273sXF4oRJ75IItlcgk= +go.opentelemetry.io/collector/featuregate v0.65.0/go.mod h1:tewuFKJYalWBU0bmNKg++MC1ipINXUr6szYzOw2p1GI= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= +go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/connector/nopconnector/nop.go b/connector/nopconnector/nop.go new file mode 100644 index 000000000000..289c439ad8c6 --- /dev/null +++ b/connector/nopconnector/nop.go @@ -0,0 +1,146 @@ +// 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 nopconnector // import "go.opentelemetry.io/collector/connector/nopconnector" + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/internal/sharedcomponent" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +const ( + typeStr = "nop" +) + +type Config struct { + config.ConnectorSettings `mapstructure:",squash"` +} + +var _ component.ConnectorConfig = (*Config)(nil) + +// NewFactory returns a ConnectorFactory. +func NewFactory() component.ConnectorFactory { + return component.NewConnectorFactory( + typeStr, + createDefaultConfig, + component.WithTracesToTracesConnector(createTracesToTracesConnector, component.StabilityLevelDevelopment), + component.WithMetricsToMetricsConnector(createMetricsToMetricsConnector, component.StabilityLevelDevelopment), + component.WithLogsToLogsConnector(createLogsToLogsConnector, component.StabilityLevelDevelopment), + ) +} + +// createDefaultConfig creates the default configuration. +func createDefaultConfig() component.ConnectorConfig { + return &Config{} +} + +// createTracesToTracesConnector creates a trace receiver based on provided config. +func createTracesToTracesConnector( + _ context.Context, + set component.ConnectorCreateSettings, + cfg component.ConnectorConfig, + nextConsumer consumer.Traces, +) (component.TracesToTracesConnector, error) { + comp := connectors.GetOrAdd(cfg.ID(), func() component.Component { + return newNopConnector(cfg.(*Config), set) + }) + + conn := comp.Unwrap().(*nopConnector) + conn.tracesConsumer = nextConsumer + return conn, nil +} + +// createMetricsToMetricsConnector creates a metrics receiver based on provided config. +func createMetricsToMetricsConnector( + _ context.Context, + set component.ConnectorCreateSettings, + cfg component.ConnectorConfig, + nextConsumer consumer.Metrics, +) (component.MetricsToMetricsConnector, error) { + comp := connectors.GetOrAdd(cfg.ID(), func() component.Component { + return newNopConnector(cfg.(*Config), set) + }) + + conn := comp.Unwrap().(*nopConnector) + conn.metricsConsumer = nextConsumer + return conn, nil +} + +// createLogsToLogsConnector creates a log receiver based on provided config. +func createLogsToLogsConnector( + _ context.Context, + set component.ConnectorCreateSettings, + cfg component.ConnectorConfig, + nextConsumer consumer.Logs, +) (component.LogsToLogsConnector, error) { + comp := connectors.GetOrAdd(cfg.ID(), func() component.Component { + return newNopConnector(cfg.(*Config), set) + }) + + conn := comp.Unwrap().(*nopConnector) + conn.logsConsumer = nextConsumer + return conn, nil +} + +// This is the map of already created nop connectors for particular configurations. +// We maintain this map because the Factory is asked trace, metric, and log receivers +// separately but they must not create separate objects. When the connector is shutdown +// it should be removed from this map so the same configuration can be recreated successfully. +var connectors = sharedcomponent.NewSharedComponents() + +// otlpReceiver is the type that exposes Trace and Metrics reception. +type nopConnector struct { + cfg *Config + + tracesConsumer consumer.Traces + metricsConsumer consumer.Metrics + logsConsumer consumer.Logs + + settings component.ConnectorCreateSettings +} + +func newNopConnector(cfg *Config, settings component.ConnectorCreateSettings) *nopConnector { + return &nopConnector{cfg: cfg, settings: settings} +} + +func (c *nopConnector) Capabilities() consumer.Capabilities { + return consumer.Capabilities{MutatesData: false} +} + +func (c *nopConnector) Start(_ context.Context, host component.Host) error { + return nil +} + +func (c *nopConnector) Shutdown(ctx context.Context) error { + return nil +} + +func (c *nopConnector) ConsumeTracesToTraces(ctx context.Context, td ptrace.Traces) error { + return c.tracesConsumer.ConsumeTraces(ctx, td) +} + +func (c *nopConnector) ConsumeMetricsToMetrics(ctx context.Context, md pmetric.Metrics) error { + return c.metricsConsumer.ConsumeMetrics(ctx, md) +} + +func (c *nopConnector) ConsumeLogsToLogs(ctx context.Context, ld plog.Logs) error { + return c.logsConsumer.ConsumeLogs(ctx, ld) +} diff --git a/connector/nopconnector/nop_test.go b/connector/nopconnector/nop_test.go new file mode 100644 index 000000000000..e71a17187caa --- /dev/null +++ b/connector/nopconnector/nop_test.go @@ -0,0 +1,73 @@ +// 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 nopconnector + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +func TestNop(t *testing.T) { + f := NewFactory() + cfg := f.CreateDefaultConfig() + assert.Equal(t, &Config{}, cfg) + + ctx := context.Background() + set := componenttest.NewNopConnectorCreateSettings() + host := componenttest.NewNopHost() + + tracesSink := new(consumertest.TracesSink) + tracesToTraces, err := f.CreateTracesToTracesConnector(ctx, set, cfg, tracesSink) + assert.NoError(t, err) + assert.NotNil(t, tracesToTraces) + + metricsSink := new(consumertest.MetricsSink) + metricsToMetrics, err := f.CreateMetricsToMetricsConnector(ctx, set, cfg, metricsSink) + assert.NoError(t, err) + assert.NotNil(t, metricsToMetrics) + + logsSink := new(consumertest.LogsSink) + logsToLogs, err := f.CreateLogsToLogsConnector(ctx, set, cfg, logsSink) + assert.NoError(t, err) + assert.NotNil(t, logsToLogs) + + assert.NoError(t, tracesToTraces.Start(ctx, host)) + assert.NoError(t, metricsToMetrics.Start(ctx, host)) + assert.NoError(t, logsToLogs.Start(ctx, host)) + + assert.NoError(t, tracesToTraces.ConsumeTracesToTraces(ctx, ptrace.NewTraces())) + + assert.NoError(t, metricsToMetrics.ConsumeMetricsToMetrics(ctx, pmetric.NewMetrics())) + assert.NoError(t, metricsToMetrics.ConsumeMetricsToMetrics(ctx, pmetric.NewMetrics())) + + assert.NoError(t, logsToLogs.ConsumeLogsToLogs(ctx, plog.NewLogs())) + assert.NoError(t, logsToLogs.ConsumeLogsToLogs(ctx, plog.NewLogs())) + assert.NoError(t, logsToLogs.ConsumeLogsToLogs(ctx, plog.NewLogs())) + + assert.NoError(t, tracesToTraces.Shutdown(ctx)) + assert.NoError(t, metricsToMetrics.Shutdown(ctx)) + assert.NoError(t, logsToLogs.Shutdown(ctx)) + + assert.Equal(t, 1, len(tracesSink.AllTraces())) + assert.Equal(t, 2, len(metricsSink.AllMetrics())) + assert.Equal(t, 3, len(logsSink.AllLogs())) +} diff --git a/go.mod b/go.mod index d14dbeee7d63..c6700811ce50 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( go.uber.org/zap v1.23.0 golang.org/x/net v0.0.0-20220722155237-a158d28d115b golang.org/x/sys v0.2.0 + gonum.org/v1/gonum v0.12.0 google.golang.org/grpc v1.51.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 7ac776e44356..0e23e469aea7 100644 --- a/go.sum +++ b/go.sum @@ -474,6 +474,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -681,6 +682,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/service/config.go b/service/config.go index de1fbc2a033c..10d021429e6f 100644 --- a/service/config.go +++ b/service/config.go @@ -43,6 +43,9 @@ type Config struct { // Extensions is a map of ComponentID to extensions. Extensions map[component.ID]component.ExtensionConfig + // Connectors is a map of ComponentID to connectors. + Connectors map[component.ID]component.ConnectorConfig + Service ConfigService } @@ -85,6 +88,20 @@ func (cfg *Config) Validate() error { } } + // Validate the connector configuration. + for connID, connCfg := range cfg.Connectors { + if err := component.ValidateConfig(connCfg); err != nil { + return fmt.Errorf("connector %q has invalid configuration: %w", connID, err) + } + + if _, ok := cfg.Exporters[connID]; ok { + return fmt.Errorf("ambiguous id: connector %q cannot have same id as exporter", connID) + } + if _, ok := cfg.Receivers[connID]; ok { + return fmt.Errorf("ambiguous id: connector %q cannot have same id as receiver", connID) + } + } + // Validate the extension configuration. for extID, extCfg := range cfg.Extensions { if err := component.ValidateConfig(extCfg); err != nil { @@ -109,6 +126,10 @@ func (cfg *Config) validateService() error { return errMissingServicePipelines } + // Keep track of whether connectors are used as receivers and exporters + connectorsAsReceivers := make(map[component.ID]struct{}, len(cfg.Connectors)) + connectorsAsExporters := make(map[component.ID]struct{}, len(cfg.Connectors)) + // Check that all pipelines have at least one receiver and one exporter, and they reference // only configured components. for pipelineID, pipeline := range cfg.Service.Pipelines { @@ -124,9 +145,14 @@ func (cfg *Config) validateService() error { // Validate pipeline receiver name references. for _, ref := range pipeline.Receivers { // Check that the name referenced in the pipeline's receivers exists in the top-level receivers. - if cfg.Receivers[ref] == nil { - return fmt.Errorf("pipeline %q references receiver %q which does not exist", pipelineID, ref) + if cfg.Receivers[ref] != nil { + continue } + if cfg.Connectors[ref] != nil { + connectorsAsReceivers[ref] = struct{}{} + continue + } + return fmt.Errorf("pipeline %q references receiver %q which does not exist", pipelineID, ref) } // Validate pipeline processor name references. @@ -152,15 +178,33 @@ func (cfg *Config) validateService() error { // Validate pipeline exporter name references. for _, ref := range pipeline.Exporters { // Check that the name referenced in the pipeline's Exporters exists in the top-level Exporters. - if cfg.Exporters[ref] == nil { - return fmt.Errorf("pipeline %q references exporter %q which does not exist", pipelineID, ref) + if cfg.Exporters[ref] != nil { + continue + } + if cfg.Connectors[ref] != nil { + connectorsAsExporters[ref] = struct{}{} + continue } + return fmt.Errorf("pipeline %q references exporter %q which does not exist", pipelineID, ref) } if err := cfg.Service.Telemetry.Validate(); err != nil { fmt.Printf("telemetry config validation failed, %v\n", err) } } + + // Validate that connectors are used as both receiver and exporter + for _, conn := range cfg.Connectors { + _, recOK := connectorsAsReceivers[conn.ID()] + _, expOK := connectorsAsExporters[conn.ID()] + if recOK && !expOK { + return fmt.Errorf("connector %q must be used as both receiver and exporter but is only used as receiver", conn.ID()) + } + if !recOK && expOK { + return fmt.Errorf("connector %q must be used as both receiver and exporter but is only used as exporter", conn.ID()) + } + } + return nil } diff --git a/service/config_provider.go b/service/config_provider.go index 3feba9741fe1..1d882b44ae01 100644 --- a/service/config_provider.go +++ b/service/config_provider.go @@ -113,6 +113,7 @@ func (cm *configProvider) Get(ctx context.Context, factories component.Factories Receivers: cfg.Receivers.GetReceivers(), Processors: cfg.Processors.GetProcessors(), Exporters: cfg.Exporters.GetExporters(), + Connectors: cfg.Connectors.GetConnectors(), Extensions: cfg.Extensions.GetExtensions(), Service: cfg.Service, }, nil diff --git a/service/config_test.go b/service/config_test.go index 6719492bb2d5..24dbc3e18fa6 100644 --- a/service/config_test.go +++ b/service/config_test.go @@ -32,6 +32,7 @@ var ( errInvalidRecvConfig = errors.New("invalid receiver config") errInvalidExpConfig = errors.New("invalid exporter config") errInvalidProcConfig = errors.New("invalid processor config") + errInvalidConnConfig = errors.New("invalid connector config") errInvalidExtConfig = errors.New("invalid extension config") ) @@ -62,6 +63,15 @@ func (nc *nopProcConfig) Validate() error { return nc.validateErr } +type nopConnConfig struct { + config.ConnectorSettings + validateErr error +} + +func (nc *nopConnConfig) Validate() error { + return nc.validateErr +} + type nopExtConfig struct { config.ExtensionSettings validateErr error @@ -158,6 +168,26 @@ func TestConfigValidate(t *testing.T) { }, expected: errors.New(`pipeline "traces" references exporter "nop/2" which does not exist`), }, + { + name: "invalid-connector-reference-as-receiver", + cfgFn: func() *Config { + cfg := generateConfig() + pipe := cfg.Service.Pipelines[component.NewID("traces")] + pipe.Receivers = append(pipe.Receivers, component.NewIDWithName("nop", "conn2")) + return cfg + }, + expected: errors.New(`pipeline "traces" references receiver "nop/conn2" which does not exist`), + }, + { + name: "invalid-connector-reference-as-receiver", + cfgFn: func() *Config { + cfg := generateConfig() + pipe := cfg.Service.Pipelines[component.NewID("traces")] + pipe.Exporters = append(pipe.Exporters, component.NewIDWithName("nop", "conn2")) + return cfg + }, + expected: errors.New(`pipeline "traces" references exporter "nop/conn2" which does not exist`), + }, { name: "missing-pipeline-receivers", cfgFn: func() *Config { @@ -178,6 +208,26 @@ func TestConfigValidate(t *testing.T) { }, expected: errors.New(`pipeline "traces" must have at least one exporter`), }, + { + name: "missing-connector-as-receiver", + cfgFn: func() *Config { + cfg := generateConfig() + pipe := cfg.Service.Pipelines[component.NewID("traces")] + pipe.Exporters = append(pipe.Exporters, component.NewIDWithName("nop", "conn")) + return cfg + }, + expected: errors.New(`connector "nop/conn" must be used as both receiver and exporter but is only used as exporter`), + }, + { + name: "missing-connector-as-exporter", + cfgFn: func() *Config { + cfg := generateConfig() + pipe := cfg.Service.Pipelines[component.NewID("traces")] + pipe.Receivers = append(pipe.Receivers, component.NewIDWithName("nop", "conn")) + return cfg + }, + expected: errors.New(`connector "nop/conn" must be used as both receiver and exporter but is only used as receiver`), + }, { name: "missing-pipelines", cfgFn: func() *Config { @@ -223,6 +273,52 @@ func TestConfigValidate(t *testing.T) { }, expected: fmt.Errorf(`processor "nop" has invalid configuration: %w`, errInvalidProcConfig), }, + { + name: "invalid-connector-config", + cfgFn: func() *Config { + cfg := generateConfig() + cfg.Connectors[component.NewIDWithName("nop", "conn")] = &nopConnConfig{ + ConnectorSettings: config.NewConnectorSettings(component.NewIDWithName("nop", "conn")), + validateErr: errInvalidConnConfig, + } + return cfg + }, + expected: fmt.Errorf(`connector "nop/conn" has invalid configuration: %w`, errInvalidConnConfig), + }, + { + name: "ambiguous-connector-name-as-receiver", + cfgFn: func() *Config { + cfg := generateConfig() + cfg.Receivers[component.NewID("nop/2")] = &nopRecvConfig{ + ReceiverSettings: config.NewReceiverSettings(component.NewIDWithName("nop", "2")), + } + cfg.Connectors[component.NewID("nop/2")] = &nopConnConfig{ + ConnectorSettings: config.NewConnectorSettings(component.NewIDWithName("nop", "2")), + } + pipe := cfg.Service.Pipelines[component.NewID("traces")] + pipe.Receivers = append(pipe.Receivers, component.NewIDWithName("nop", "2")) + pipe.Exporters = append(pipe.Exporters, component.NewIDWithName("nop", "2")) + return cfg + }, + expected: errors.New(`ambiguous id: connector "nop/2" cannot have same id as receiver`), + }, + { + name: "ambiguous-connector-name-as-exporter", + cfgFn: func() *Config { + cfg := generateConfig() + cfg.Exporters[component.NewID("nop/2")] = &nopExpConfig{ + ExporterSettings: config.NewExporterSettings(component.NewIDWithName("nop", "2")), + } + cfg.Connectors[component.NewID("nop/2")] = &nopConnConfig{ + ConnectorSettings: config.NewConnectorSettings(component.NewIDWithName("nop", "2")), + } + pipe := cfg.Service.Pipelines[component.NewID("traces")] + pipe.Receivers = append(pipe.Receivers, component.NewIDWithName("nop", "2")) + pipe.Exporters = append(pipe.Exporters, component.NewIDWithName("nop", "2")) + return cfg + }, + expected: errors.New(`ambiguous id: connector "nop/2" cannot have same id as exporter`), + }, { name: "invalid-extension-config", cfgFn: func() *Config { @@ -290,6 +386,11 @@ func generateConfig() *Config { ExtensionSettings: config.NewExtensionSettings(component.NewID("nop")), }, }, + Connectors: map[component.ID]component.ConnectorConfig{ + component.NewIDWithName("nop", "conn"): &nopConnConfig{ + ConnectorSettings: config.NewConnectorSettings(component.NewIDWithName("nop", "conn")), + }, + }, Service: ConfigService{ Telemetry: telemetry.Config{ Logs: telemetry.LogsConfig{ diff --git a/service/host.go b/service/host.go index 607cb0c6b850..38736997b3e8 100644 --- a/service/host.go +++ b/service/host.go @@ -27,7 +27,7 @@ type serviceHost struct { factories component.Factories buildInfo component.BuildInfo - pipelines *pipelines.Pipelines + pipelines pipelines.Pipelines extensions *extensions.Extensions } diff --git a/service/internal/components/constants.go b/service/internal/components/constants.go index b1bed541de98..dfaaed1c7634 100644 --- a/service/internal/components/constants.go +++ b/service/internal/components/constants.go @@ -15,13 +15,15 @@ package components // import "go.opentelemetry.io/collector/service/internal/components" const ( - ZapKindKey = "kind" - ZapKindReceiver = "receiver" - ZapKindProcessor = "processor" - ZapKindExporter = "exporter" - ZapKindExtension = "extension" - ZapKindPipeline = "pipeline" - ZapNameKey = "name" - ZapDataTypeKey = "data_type" - ZapStabilityKey = "stability" + ZapKindKey = "kind" + ZapKindReceiver = "receiver" + ZapKindProcessor = "processor" + ZapKindExporter = "exporter" + ZapKindExtension = "extension" + ZapKindPipeline = "pipeline" + ZapNameKey = "name" + ZapDataTypeKey = "data_type" + ZapStabilityKey = "stability" + ZapRoleExporterInPipeline = "as_exporter_in_pipeline" + ZapRoleReceiverInPipeline = "as_receiver_in_pipeline" ) diff --git a/service/internal/configunmarshaler/connectors.go b/service/internal/configunmarshaler/connectors.go new file mode 100644 index 000000000000..a17904ce4b4b --- /dev/null +++ b/service/internal/configunmarshaler/connectors.go @@ -0,0 +1,72 @@ +// 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 configunmarshaler // import "go.opentelemetry.io/collector/service/internal/configunmarshaler" + +import ( + "reflect" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" +) + +// connectorsKeyName is the configuration key name for connectors section. +const connectorsKeyName = "connectors" + +type Connectors struct { + conns map[component.ID]component.ConnectorConfig + + factories map[component.Type]component.ConnectorFactory +} + +func NewConnectors(factories map[component.Type]component.ConnectorFactory) *Connectors { + return &Connectors{factories: factories} +} + +func (c *Connectors) Unmarshal(conf *confmap.Conf) error { + rawConns := make(map[component.ID]map[string]interface{}) + if err := conf.Unmarshal(&rawConns, confmap.WithErrorUnused()); err != nil { + return err + } + + // Prepare resulting map. + c.conns = make(map[component.ID]component.ConnectorConfig) + + // Iterate over Connectors and create a config for each. + for id, value := range rawConns { + // Find connector factory based on "type" that we read from config source. + factory := c.factories[id.Type()] + if factory == nil { + return errorUnknownType(connectorsKeyName, id, reflect.ValueOf(c.factories).MapKeys()) + } + + // Create the default config for this exporter. + connectorCfg := factory.CreateDefaultConfig() + connectorCfg.SetIDName(id.Name()) + + // Now that the default config struct is created we can Unmarshal into it, + // and it will apply user-defined config on top of the default. + if err := component.UnmarshalConnectorConfig(confmap.NewFromStringMap(value), connectorCfg); err != nil { + return errorUnmarshalError(connectorsKeyName, id, err) + } + + c.conns[id] = connectorCfg + } + + return nil +} + +func (c *Connectors) GetConnectors() map[component.ID]component.ConnectorConfig { + return c.conns +} diff --git a/service/internal/configunmarshaler/connectors_test.go b/service/internal/configunmarshaler/connectors_test.go new file mode 100644 index 000000000000..2d633aec4fc2 --- /dev/null +++ b/service/internal/configunmarshaler/connectors_test.go @@ -0,0 +1,114 @@ +// 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 configunmarshaler + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/confmap" +) + +func TestConnectorsUnmarshal(t *testing.T) { + factories, err := componenttest.NopFactories() + require.NoError(t, err) + + conns := NewConnectors(factories.Connectors) + conf := confmap.NewFromStringMap(map[string]interface{}{ + "nop": nil, + "nop/myconnector": nil, + }) + require.NoError(t, conns.Unmarshal(conf)) + + cfgWithName := factories.Connectors["nop"].CreateDefaultConfig() + cfgWithName.SetIDName("myconnector") + assert.Equal(t, map[component.ID]component.ConnectorConfig{ + component.NewID("nop"): factories.Connectors["nop"].CreateDefaultConfig(), + component.NewIDWithName("nop", "myconnector"): cfgWithName, + }, conns.GetConnectors()) +} + +func TestConnectorsUnmarshalError(t *testing.T) { + var testCases = []struct { + name string + conf *confmap.Conf + // string that the error must contain + expectedError string + }{ + { + name: "invalid-connector-type", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": nil, + "/custom": nil, + }), + expectedError: "the part before / should not be empty", + }, + { + name: "invalid-connector-name-after-slash", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": nil, + "nop/": nil, + }), + expectedError: "the part after / should not be empty", + }, + { + name: "unknown-connector-type", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nosuchconnector": nil, + }), + expectedError: "unknown connectors type: \"nosuchconnector\"", + }, + { + name: "duplicate-connector", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop /exp ": nil, + " nop/ exp": nil, + }), + expectedError: "duplicate name", + }, + { + name: "invalid-connector-section", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": map[string]interface{}{ + "unknown_section": "connector", + }, + }), + expectedError: "error reading connectors configuration for \"nop\"", + }, + { + name: "invalid-connector-sub-config", + conf: confmap.NewFromStringMap(map[string]interface{}{ + "nop": "tests", + }), + expectedError: "'[nop]' expected a map, got 'string'", + }, + } + + factories, err := componenttest.NopFactories() + assert.NoError(t, err) + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + exps := NewConnectors(factories.Connectors) + err = exps.Unmarshal(tt.conf) + require.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + }) + } +} diff --git a/service/internal/pipelines/graph.go b/service/internal/pipelines/graph.go new file mode 100644 index 000000000000..7e4f5e9e875c --- /dev/null +++ b/service/internal/pipelines/graph.go @@ -0,0 +1,554 @@ +// 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 pipelines // import "go.opentelemetry.io/collector/service/internal/pipelines" + +import ( + "context" + "fmt" + "net/http" + "sort" + + "gonum.org/v1/gonum/graph" + "gonum.org/v1/gonum/graph/simple" + "gonum.org/v1/gonum/graph/topo" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/service/internal/zpages" +) + +type pipelinesGraph struct { + + // All component instances represented as nodes, with directed edges indicating data flow. + componentGraph *simple.DirectedGraph + + // Keep track of how nodes relate to pipelines, so we can declare edges in the graph. + pipelineGraphs map[component.ID]*pipelineGraph +} + +func (g *pipelinesGraph) StartAll(ctx context.Context, host component.Host) error { + nodes, err := topo.Sort(g.componentGraph) + if err != nil { + return err + } + + // Start exporters first, and work towards receivers + for i := len(nodes) - 1; i >= 0; i-- { + comp, ok := nodes[i].(component.Component) + if !ok { + continue + } + if compErr := comp.Start(ctx, host); compErr != nil { + return compErr + } + } + return nil +} + +func (g *pipelinesGraph) ShutdownAll(ctx context.Context) error { + nodes, err := topo.Sort(g.componentGraph) + if err != nil { + return err + } + + // Stop receivers first, and work towards exporters + for i := 0; i < len(nodes); i++ { + comp, ok := nodes[i].(component.Component) + if !ok { + continue + } + if compErr := comp.Shutdown(ctx); compErr != nil { + return compErr + } + } + return nil +} + +func NewPipelinesGraph(ctx context.Context, set Settings) (Pipelines, error) { + pipelines := &pipelinesGraph{ + componentGraph: simple.NewDirectedGraph(), + pipelineGraphs: make(map[component.ID]*pipelineGraph, len(set.PipelineConfigs)), + } + for pipelineID := range set.PipelineConfigs { + pipelines.pipelineGraphs[pipelineID] = newPipelineGraph() + } + + if err := pipelines.createNodes(set); err != nil { + return nil, err + } + + pipelines.createEdges() + + if err := pipelines.buildNodes(ctx, set.Telemetry, set.BuildInfo); err != nil { + return nil, err + } + + return pipelines, nil +} + +// Creates a node for each instance of a component and adds it to the graph +func (g *pipelinesGraph) createNodes(set Settings) error { + + // map[connectorID]pipelineIDs + // Keep track of connectors and where they are used. + connectorsAsExporter := make(map[component.ID][]component.ID) + connectorsAsReceiver := make(map[component.ID][]component.ID) + + for pipelineID, pipelineCfg := range set.PipelineConfigs { + for _, recvID := range pipelineCfg.Receivers { + if _, isConnector := set.ConnectorConfigs[recvID]; isConnector { + connectorsAsReceiver[recvID] = append(connectorsAsReceiver[recvID], pipelineID) + continue + } + if err := g.addReceiver(pipelineID, recvID, set.ReceiverConfigs, set.ReceiverFactories); err != nil { + return err + } + } + + for _, procID := range pipelineCfg.Processors { + if err := g.addProcessor(pipelineID, procID, set.ProcessorConfigs, set.ProcessorFactories); err != nil { + return err + } + } + + for _, exprID := range pipelineCfg.Exporters { + if _, isConnector := set.ConnectorConfigs[exprID]; isConnector { + connectorsAsExporter[exprID] = append(connectorsAsExporter[exprID], pipelineID) + continue + } + if err := g.addExporter(pipelineID, exprID, set.ExporterConfigs, set.ExporterFactories); err != nil { + return err + } + } + } + + return g.addConnectors(connectorsAsExporter, connectorsAsReceiver, set) +} + +func (g *pipelinesGraph) addReceiver( + pipelineID, recvID component.ID, + cfgs map[component.ID]component.ReceiverConfig, + factories map[component.Type]component.ReceiverFactory, +) error { + receiverNodeID := newReceiverNodeID(pipelineID.Type(), recvID) + + if rcvrNode := g.componentGraph.Node(receiverNodeID.ID()); rcvrNode != nil { + g.pipelineGraphs[pipelineID].addReceiver(rcvrNode) + return nil + } + + cfg, existsCfg := cfgs[recvID] + if !existsCfg { + return fmt.Errorf("receiver %q is not configured", recvID) + } + + factory, existsFactory := factories[recvID.Type()] + if !existsFactory { + return fmt.Errorf("receiver factory not available for: %q", recvID) + } + + node := &receiverNode{ + componentNodeID: receiverNodeID, + componentID: recvID, + pipelineType: pipelineID.Type(), + cfg: cfg, + factory: factory, + } + g.pipelineGraphs[pipelineID].addReceiver(node) + g.componentGraph.AddNode(node) + return nil +} + +func (g *pipelinesGraph) addProcessor( + pipelineID, procID component.ID, + cfgs map[component.ID]component.ProcessorConfig, + factories map[component.Type]component.ProcessorFactory, +) error { + cfg, existsCfg := cfgs[procID] + if !existsCfg { + return fmt.Errorf("processor %q is not configured", procID) + } + + factory, existsFactory := factories[procID.Type()] + if !existsFactory { + return fmt.Errorf("receiver factory not available for: %q", procID) + } + + node := &processorNode{ + componentNodeID: newProcessorNodeID(pipelineID, procID), + componentID: procID, + pipelineID: pipelineID, + cfg: cfg, + factory: factory, + } + g.pipelineGraphs[pipelineID].addProcessor(node) + g.componentGraph.AddNode(node) + return nil +} + +func (g *pipelinesGraph) addExporter( + pipelineID, exprID component.ID, + cfgs map[component.ID]component.ExporterConfig, + factories map[component.Type]component.ExporterFactory, +) error { + exporterNodeID := newExporterNodeID(pipelineID.Type(), exprID) + + if expNode := g.componentGraph.Node(exporterNodeID.ID()); expNode != nil { + g.pipelineGraphs[pipelineID].addExporter(expNode) + return nil + } + + cfg, existsCfg := cfgs[exprID] + if !existsCfg { + return fmt.Errorf("exporter %q is not configured", exprID) + } + + factory, ok := factories[exprID.Type()] + if !ok { + return fmt.Errorf("exporter factory not available for: %q", exprID) + } + + node := &exporterNode{ + componentNodeID: exporterNodeID, + componentID: exprID, + pipelineType: pipelineID.Type(), + cfg: cfg, + factory: factory, + } + g.pipelineGraphs[pipelineID].addExporter(node) + g.componentGraph.AddNode(node) + return nil +} + +func (g *pipelinesGraph) addConnectors(asExporter, asReceiver map[component.ID][]component.ID, set Settings) error { + if len(asExporter) != len(asReceiver) { + return fmt.Errorf("each connector must be used as both receiver and exporter") + } + + // For each pair of pipelines that the connector connects, check if the + // data type combination is supported. If so, create a connector. + // A separate instance is created so that the fanoutprocessor will correctly + // replicate signals to each connector as if it were a separate exporter. + for connID, exprPipelineIDs := range asExporter { + rcvrPipelineIDs, ok := asReceiver[connID] + if !ok { + return fmt.Errorf("connector %q must be used as receiver, only found as exporter", connID) + } + + factory, ok := set.ConnectorFactories[connID.Type()] + if !ok { + return fmt.Errorf("connector factory not available for: %q", connID) + } + + var foundValidDataTypes bool + for _, eID := range exprPipelineIDs { + for _, rID := range rcvrPipelineIDs { + stabilityLevel := getConnectorStabilityLevel(factory, eID.Type(), rID.Type()) + if stabilityLevel == component.StabilityLevelUndefined { + continue + } + foundValidDataTypes = true + if err := g.addConnector(eID, rID, connID, set.ConnectorConfigs, set.ConnectorFactories); err != nil { + return err + } + } + } + if !foundValidDataTypes { + return fmt.Errorf("connector %q not used between valid pipeline types", connID) + } + } + + return nil +} + +func (g *pipelinesGraph) addConnector( + exprPipelineID, rcvrPipelineID, connID component.ID, + cfgs map[component.ID]component.ConnectorConfig, + factories map[component.Type]component.ConnectorFactory, +) error { + connectorNodeID := newConnectorNodeID(exprPipelineID.Type(), rcvrPipelineID.Type(), connID) + + if connNode := g.componentGraph.Node(connectorNodeID.ID()); connNode != nil { + g.pipelineGraphs[exprPipelineID].addExporter(connNode) + g.pipelineGraphs[rcvrPipelineID].addReceiver(connNode) + return nil + } + + cfg, existsCfg := cfgs[connID] + if !existsCfg { + return fmt.Errorf("connector %q is not configured", connID) + } + + factory, ok := factories[connID.Type()] + if !ok { + return fmt.Errorf("connector factory not available for: %q", connID) + } + + node := &connectorNode{ + componentNodeID: connectorNodeID, + componentID: connID, + exprPipelineType: exprPipelineID.Type(), + rcvrPipelineType: rcvrPipelineID.Type(), + cfg: cfg, + factory: factory, + } + g.pipelineGraphs[exprPipelineID].addExporter(node) + g.pipelineGraphs[rcvrPipelineID].addReceiver(node) + g.componentGraph.AddNode(node) + return nil +} + +func (g *pipelinesGraph) createEdges() { + for pipelineID, pg := range g.pipelineGraphs { + fanOutToExporters := newFanOutNode(pipelineID) + + for _, exporter := range pg.exporters { + g.componentGraph.SetEdge(g.componentGraph.NewEdge(fanOutToExporters, exporter)) + } + + if len(pg.processors) == 0 { + for _, receiver := range pg.receivers { + g.componentGraph.SetEdge(g.componentGraph.NewEdge(receiver, fanOutToExporters)) + } + continue + } + + fanInToProcessors := newFanInNode(pipelineID) + + for _, receiver := range pg.receivers { + g.componentGraph.SetEdge(g.componentGraph.NewEdge(receiver, fanInToProcessors)) + } + + g.componentGraph.SetEdge(g.componentGraph.NewEdge(fanInToProcessors, pg.processors[0])) + + for i := 0; i+1 < len(pg.processors); i++ { + g.componentGraph.SetEdge(g.componentGraph.NewEdge(pg.processors[i], pg.processors[i+1])) + } + + g.componentGraph.SetEdge(g.componentGraph.NewEdge(pg.processors[len(pg.processors)-1], fanOutToExporters)) + } +} + +func (g *pipelinesGraph) buildNodes(ctx context.Context, tel component.TelemetrySettings, info component.BuildInfo) error { + nodes, err := topo.Sort(g.componentGraph) + if err != nil { + return err + } + + for i := len(nodes) - 1; i >= 0; i-- { + node := nodes[i] + switch n := node.(type) { + case *receiverNode: + nexts := g.nextConsumers(n.ID()) + if len(nexts) == 0 { + return fmt.Errorf("receiver %q has no next consumer: %w", n.componentID, err) + } + err = n.build(ctx, tel, info, nexts) + if err != nil { + return err + } + case *processorNode: + nexts := g.nextConsumers(n.ID()) + if len(nexts) == 0 { + return fmt.Errorf("processor %q has no next consumer: %w", n.componentID, err) + } + if len(nexts) > 1 { + return fmt.Errorf("processor %q has multiple consumers", n.componentID) + } + err = n.build(ctx, tel, info, nexts[0]) + if err != nil { + return err + } + case *connectorNode: + nexts := g.nextConsumers(n.ID()) + if len(nexts) == 0 { + return fmt.Errorf("connector %q has no next consumer: %w", n.componentID, err) + } + err = n.build(ctx, tel, info, nexts) + if err != nil { + return err + } + case *fanInNode: + nexts := g.nextConsumers(n.ID()) + if len(nexts) != 1 { + return fmt.Errorf("fan-in in pipeline %q must have one consumer: %w", n.pipelineID, err) + } + n.build(nexts[0], g.nextProcessors(n.ID())) + case *fanOutNode: + nexts := g.nextConsumers(n.ID()) + if len(nexts) == 0 { + return fmt.Errorf("fan-out in pipeline %q has no next consumer: %w", n.pipelineID, err) + } + err = n.build(nexts) + if err != nil { + return err + } + case *exporterNode: + err = n.build(ctx, tel, info) + if err != nil { + return err + } + } + } + return nil +} + +func (g *pipelinesGraph) nextConsumers(nodeID int64) []baseConsumer { + nextNodes := g.componentGraph.From(nodeID) + nextConsumers := make([]baseConsumer, 0, nextNodes.Len()) + for nextNodes.Next() { + switch next := nextNodes.Node().(type) { + case *processorNode: + nextConsumers = append(nextConsumers, next.Component.(baseConsumer)) + case *exporterNode: + nextConsumers = append(nextConsumers, next.Component.(baseConsumer)) + case *connectorNode: + nextConsumers = append(nextConsumers, next.Component.(baseConsumer)) + case *fanInNode: + nextConsumers = append(nextConsumers, next.baseConsumer) + case *fanOutNode: + nextConsumers = append(nextConsumers, next.baseConsumer) + default: + panic(fmt.Sprintf("type cannot be consumer: %T", next)) + } + } + return nextConsumers +} + +func (g *pipelinesGraph) nextProcessors(nodeID int64) []*processorNode { + nextProcessors := make([]*processorNode, 0) + for { + nextNodes := g.componentGraph.From(nodeID) + if nextNodes.Len() != 1 { + break + } + procNode, ok := nextNodes.Node().(*processorNode) + if !ok { + break + } + nextProcessors = append(nextProcessors, procNode) + } + return nextProcessors +} + +// A node-based representation of a pipeline configuration. +type pipelineGraph struct { + + // Use maps for receivers and exporters to assist with deduplication of connector instances. + receivers map[int64]graph.Node + exporters map[int64]graph.Node + + // The order of processors is very important. Therefore use a slice for processors. + processors []graph.Node +} + +func newPipelineGraph() *pipelineGraph { + return &pipelineGraph{ + receivers: make(map[int64]graph.Node), + exporters: make(map[int64]graph.Node), + } +} + +func (p *pipelineGraph) addReceiver(node graph.Node) { + p.receivers[node.ID()] = node +} +func (p *pipelineGraph) addProcessor(node graph.Node) { + p.processors = append(p.processors, node) +} +func (p *pipelineGraph) addExporter(node graph.Node) { + p.exporters[node.ID()] = node +} + +func (g *pipelinesGraph) GetExporters() map[component.DataType]map[component.ID]component.Component { + exportersMap := make(map[component.DataType]map[component.ID]component.Component) + exportersMap[component.DataTypeTraces] = make(map[component.ID]component.Component) + exportersMap[component.DataTypeMetrics] = make(map[component.ID]component.Component) + exportersMap[component.DataTypeLogs] = make(map[component.ID]component.Component) + + for _, pg := range g.pipelineGraphs { + for _, expNode := range pg.exporters { + expOrConnNode := g.componentGraph.Node(expNode.ID()) + expNode, ok := expOrConnNode.(*exporterNode) + if !ok { + continue + } + exportersMap[expNode.pipelineType][expNode.componentID] = expNode.Component + } + } + return exportersMap +} + +func (g *pipelinesGraph) HandleZPages(w http.ResponseWriter, r *http.Request) { + qValues := r.URL.Query() + pipelineName := qValues.Get(zPipelineName) + componentName := qValues.Get(zComponentName) + componentKind := qValues.Get(zComponentKind) + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Pipelines"}) + zpages.WriteHTMLPipelinesSummaryTable(w, g.getPipelinesSummaryTableData()) + if pipelineName != "" && componentName != "" && componentKind != "" { + fullName := componentName + if componentKind == "processor" { + fullName = pipelineName + "/" + componentName + } + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: componentKind + ": " + fullName, + }) + // TODO: Add config + status info. + } + zpages.WriteHTMLPageFooter(w) +} + +func (g *pipelinesGraph) getPipelinesSummaryTableData() zpages.SummaryPipelinesTableData { + sumData := zpages.SummaryPipelinesTableData{} + sumData.Rows = make([]zpages.SummaryPipelinesTableRowData, 0, len(g.pipelineGraphs)) + + for pipelineID, pipelineGraph := range g.pipelineGraphs { + row := zpages.SummaryPipelinesTableRowData{ + FullName: pipelineID.String(), + InputType: string(pipelineID.Type()), + Receivers: make([]string, len(pipelineGraph.receivers)), + Processors: make([]string, len(pipelineGraph.processors)), + Exporters: make([]string, len(pipelineGraph.exporters)), + } + for _, recvNode := range pipelineGraph.receivers { + switch node := recvNode.(type) { + case *receiverNode: + row.Receivers = append(row.Receivers, node.componentID.String()) + case *connectorNode: + row.Receivers = append(row.Receivers, node.componentID.String()+" (connector)") + } + } + for _, procNode := range pipelineGraph.processors { + node := procNode.(*processorNode) + row.Processors = append(row.Processors, node.componentID.String()) + row.MutatesData = row.MutatesData || node.Component.(baseConsumer).Capabilities().MutatesData + } + for _, expNode := range pipelineGraph.exporters { + switch node := expNode.(type) { + case *exporterNode: + row.Exporters = append(row.Exporters, node.componentID.String()) + case *connectorNode: + row.Exporters = append(row.Exporters, node.componentID.String()+" (connector)") + } + } + sumData.Rows = append(sumData.Rows, row) + } + + sort.Slice(sumData.Rows, func(i, j int) bool { + return sumData.Rows[i].FullName < sumData.Rows[j].FullName + }) + return sumData +} diff --git a/service/internal/pipelines/graph_test.go b/service/internal/pipelines/graph_test.go new file mode 100644 index 000000000000..2d0d871d9f56 --- /dev/null +++ b/service/internal/pipelines/graph_test.go @@ -0,0 +1,1018 @@ +// 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 pipelines + +import ( + "context" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/internal/testdata" + "go.opentelemetry.io/collector/service/internal/testcomponents" +) + +// pipelineSpec is designed for easy definition of expected pipeline structure +// It represents the structure of an individual pipeline, including instanced connectors +type pipelineSpec struct { + id component.ID + receiverIDs map[int64]component.ID + processorIDs map[int64]component.ID + exporterIDs map[int64]component.ID +} + +func newPipelineSpec(id component.ID) *pipelineSpec { + return &pipelineSpec{ + id: id, + receiverIDs: make(map[int64]component.ID), + processorIDs: make(map[int64]component.ID), + exporterIDs: make(map[int64]component.ID), + } +} + +func (ps *pipelineSpec) withExampleReceiver(name string) *pipelineSpec { + rID := component.NewIDWithName(component.Type("examplereceiver"), name) + ps.receiverIDs[newReceiverNodeID(ps.id.Type(), rID).ID()] = rID + return ps +} + +func (ps *pipelineSpec) withExampleProcessor(name string) *pipelineSpec { + pID := component.NewIDWithName(component.Type("exampleprocessor"), name) + ps.processorIDs[newProcessorNodeID(ps.id, pID).ID()] = pID + return ps +} + +func (ps *pipelineSpec) withExampleExporter(name string) *pipelineSpec { + eID := component.NewIDWithName(component.Type("exampleexporter"), name) + ps.exporterIDs[newExporterNodeID(ps.id.Type(), eID).ID()] = eID + return ps +} + +func (ps *pipelineSpec) withExampleConnectorAsReceiver(name string, fromType component.Type) *pipelineSpec { + cID := component.NewIDWithName(component.Type("exampleconnector"), name) + ps.receiverIDs[newConnectorNodeID(fromType, ps.id.Type(), cID).ID()] = cID + return ps +} + +func (ps *pipelineSpec) withExampleConnectorAsExporter(name string, toType component.Type) *pipelineSpec { + cID := component.NewIDWithName(component.Type("exampleconnector"), name) + ps.exporterIDs[newConnectorNodeID(ps.id.Type(), toType, cID).ID()] = cID + return ps +} + +type pipelineSpecSlice []*pipelineSpec + +func (psSlice pipelineSpecSlice) toMap() map[component.ID]*pipelineSpec { + psMap := make(map[component.ID]*pipelineSpec, len(psSlice)) + for _, ps := range psSlice { + psMap[ps.id] = ps + } + return psMap +} + +type statefulComponentPipeline struct { + receivers map[int64]testcomponents.StatefulComponent + processors map[int64]testcomponents.StatefulComponent + exporters map[int64]testcomponents.StatefulComponent +} + +// Extract the component from each node in the pipeline and make it available as StatefulComponent +func (pg *pipelineGraph) toStatefulComponentPipeline() *statefulComponentPipeline { + statefulPipeline := &statefulComponentPipeline{ + receivers: make(map[int64]testcomponents.StatefulComponent), + processors: make(map[int64]testcomponents.StatefulComponent), + exporters: make(map[int64]testcomponents.StatefulComponent), + } + + for _, r := range pg.receivers { + switch c := r.(type) { + case *receiverNode: + statefulPipeline.receivers[c.ID()] = c.Component.(*testcomponents.ExampleReceiver) + case *connectorNode: + // connector needs to be unwrapped to access component as ExampleConnector + switch ct := c.Component.(type) { + case connectorConsumerTraces: + statefulPipeline.receivers[c.ID()] = ct.Component.(*testcomponents.ExampleConnector) + case connectorConsumerMetrics: + statefulPipeline.receivers[c.ID()] = ct.Component.(*testcomponents.ExampleConnector) + case connectorConsumerLogs: + statefulPipeline.receivers[c.ID()] = ct.Component.(*testcomponents.ExampleConnector) + } + } + } + + for _, p := range pg.processors { + pn := p.(*processorNode) + statefulPipeline.processors[pn.ID()] = pn.Component.(*testcomponents.ExampleProcessor) + } + + for _, e := range pg.exporters { + switch c := e.(type) { + case *exporterNode: + statefulPipeline.exporters[c.ID()] = c.Component.(*testcomponents.ExampleExporter) + case *connectorNode: + // connector needs to be unwrapped to access component as ExampleConnector + switch ct := c.Component.(type) { + case connectorConsumerTraces: + statefulPipeline.exporters[c.ID()] = ct.Component.(*testcomponents.ExampleConnector) + case connectorConsumerMetrics: + statefulPipeline.exporters[c.ID()] = ct.Component.(*testcomponents.ExampleConnector) + case connectorConsumerLogs: + statefulPipeline.exporters[c.ID()] = ct.Component.(*testcomponents.ExampleConnector) + } + } + } + + return statefulPipeline +} + +func TestConnectorPipelinesGraph(t *testing.T) { + // Even though we have a graph of nodes, it is very important that the notion of + // pipelines is fully respected. Therefore, test expectations are defined on a + // per-pipeline basis. Validation is also focused on each pipeline, but the net + // effect is that the entire graph is carefully validated. + tests := []struct { + name string + pipelines pipelineSpecSlice + expectedPerExporter int // requires symmetry in pipelines + }{ + // Same test cases as old test + { + name: "pipelines_simple.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_simple_multi_proc.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver(""). + withExampleProcessor("").withExampleProcessor("1"). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver(""). + withExampleProcessor("").withExampleProcessor("1"). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver(""). + withExampleProcessor("").withExampleProcessor("1"). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_simple_no_proc.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_multi.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver("").withExampleReceiver("1"). + withExampleProcessor("").withExampleProcessor("1"). + withExampleExporter("").withExampleExporter("1"), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver("").withExampleReceiver("1"). + withExampleProcessor("").withExampleProcessor("1"). + withExampleExporter("").withExampleExporter("1"), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver("").withExampleReceiver("1"). + withExampleProcessor("").withExampleProcessor("1"). + withExampleExporter("").withExampleExporter("1"), + }, + expectedPerExporter: 2, + }, + { + name: "pipelines_multi_no_proc.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver("").withExampleReceiver("1"). + withExampleExporter("").withExampleExporter("1"), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver("").withExampleReceiver("1"). + withExampleExporter("").withExampleExporter("1"), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver("").withExampleReceiver("1"). + withExampleExporter("").withExampleExporter("1"), + }, + expectedPerExporter: 2, + }, + { + name: "pipelines_exporter_multi_pipeline.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "1")). + withExampleReceiver(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "1")). + withExampleReceiver(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "1")). + withExampleReceiver(""). + withExampleExporter(""), + }, + expectedPerExporter: 2, + }, + // New test cases involving connectors + { + name: "pipelines_conn_simple_traces.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "in")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeTraces), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "out")). + withExampleConnectorAsReceiver("", component.DataTypeTraces). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_conn_simple_metrics.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "in")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeMetrics), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "out")). + withExampleConnectorAsReceiver("", component.DataTypeMetrics). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_conn_simple_logs.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "in")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "out")). + withExampleConnectorAsReceiver("", component.DataTypeLogs). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_conn_fork_merge_traces.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "in")). + withExampleReceiver(""). + withExampleProcessor("prefork"). + withExampleConnectorAsExporter("fork", component.DataTypeTraces), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "type0")). + withExampleConnectorAsReceiver("fork", component.DataTypeTraces). + withExampleProcessor("type0"). + withExampleConnectorAsExporter("merge", component.DataTypeTraces), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "type1")). + withExampleConnectorAsReceiver("fork", component.DataTypeTraces). + withExampleProcessor("type1"). + withExampleConnectorAsExporter("merge", component.DataTypeTraces), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "out")). + withExampleConnectorAsReceiver("merge", component.DataTypeTraces). + withExampleProcessor("postmerge"). + withExampleExporter(""), + }, + expectedPerExporter: 2, + }, + { + name: "pipelines_conn_fork_merge_metrics.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "in")). + withExampleReceiver(""). + withExampleProcessor("prefork"). + withExampleConnectorAsExporter("fork", component.DataTypeMetrics), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "type0")). + withExampleConnectorAsReceiver("fork", component.DataTypeMetrics). + withExampleProcessor("type0"). + withExampleConnectorAsExporter("merge", component.DataTypeMetrics), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "type1")). + withExampleConnectorAsReceiver("fork", component.DataTypeMetrics). + withExampleProcessor("type1"). + withExampleConnectorAsExporter("merge", component.DataTypeMetrics), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "out")). + withExampleConnectorAsReceiver("merge", component.DataTypeMetrics). + withExampleProcessor("postmerge"). + withExampleExporter(""), + }, + expectedPerExporter: 2, + }, + { + name: "pipelines_conn_fork_merge_logs.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "in")). + withExampleReceiver(""). + withExampleProcessor("prefork"). + withExampleConnectorAsExporter("fork", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "type0")). + withExampleConnectorAsReceiver("fork", component.DataTypeLogs). + withExampleProcessor("type0"). + withExampleConnectorAsExporter("merge", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "type1")). + withExampleConnectorAsReceiver("fork", component.DataTypeLogs). + withExampleProcessor("type1"). + withExampleConnectorAsExporter("merge", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "out")). + withExampleConnectorAsReceiver("merge", component.DataTypeLogs). + withExampleProcessor("postmerge"). + withExampleExporter(""), + }, + expectedPerExporter: 2, + }, + { + name: "pipelines_conn_translate_from_traces.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeMetrics). + withExampleConnectorAsExporter("", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleConnectorAsReceiver("", component.DataTypeTraces). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleConnectorAsReceiver("", component.DataTypeTraces). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_conn_translate_from_metrics.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeTraces). + withExampleConnectorAsExporter("", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleConnectorAsReceiver("", component.DataTypeMetrics). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleConnectorAsReceiver("", component.DataTypeMetrics). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_conn_translate_from_logs.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeTraces). + withExampleConnectorAsExporter("", component.DataTypeMetrics), + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "")). + withExampleConnectorAsReceiver("", component.DataTypeLogs). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "")). + withExampleConnectorAsReceiver("", component.DataTypeLogs). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 1, + }, + { + name: "pipelines_conn_matrix.yaml", + pipelines: pipelineSpecSlice{ + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "in")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeTraces). + withExampleConnectorAsExporter("", component.DataTypeMetrics). + withExampleConnectorAsExporter("", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "in")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeTraces). + withExampleConnectorAsExporter("", component.DataTypeMetrics). + withExampleConnectorAsExporter("", component.DataTypeLogs), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "in")). + withExampleReceiver(""). + withExampleProcessor(""). + withExampleConnectorAsExporter("", component.DataTypeTraces). + withExampleConnectorAsExporter("", component.DataTypeMetrics). + withExampleConnectorAsExporter("", component.DataTypeLogs), + + newPipelineSpec(component.NewIDWithName(component.DataTypeTraces, "out")). + withExampleConnectorAsReceiver("", component.DataTypeTraces). + withExampleConnectorAsReceiver("", component.DataTypeMetrics). + withExampleConnectorAsReceiver("", component.DataTypeLogs). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeMetrics, "out")). + withExampleConnectorAsReceiver("", component.DataTypeTraces). + withExampleConnectorAsReceiver("", component.DataTypeMetrics). + withExampleConnectorAsReceiver("", component.DataTypeLogs). + withExampleProcessor(""). + withExampleExporter(""), + newPipelineSpec(component.NewIDWithName(component.DataTypeLogs, "out")). + withExampleConnectorAsReceiver("", component.DataTypeTraces). + withExampleConnectorAsReceiver("", component.DataTypeMetrics). + withExampleConnectorAsReceiver("", component.DataTypeLogs). + withExampleProcessor(""). + withExampleExporter(""), + }, + expectedPerExporter: 3, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + factories, err := testcomponents.ExampleComponents() + assert.NoError(t, err) + + cfg := loadConfig(t, filepath.Join("testdata", test.name), factories) + + // Build the pipeline + pipelinesInterface, err := NewPipelinesGraph(context.Background(), toSettings(factories, cfg)) + require.NoError(t, err) + + allPipelines, ok := pipelinesInterface.(*pipelinesGraph) + require.True(t, ok) + + assert.Equal(t, len(test.pipelines), len(allPipelines.pipelineGraphs)) + + // The entire graph of components is started topologically + assert.NoError(t, allPipelines.StartAll(context.Background(), componenttest.NewNopHost())) + + // Check each pipeline individually, ensuring that all components are started + // and that they have observed no signals yet. + for pipelineID, pipeSpec := range test.pipelines.toMap() { + pipeline, ok := allPipelines.pipelineGraphs[pipelineID] + require.True(t, ok, "expected to find pipeline: %s", pipelineID.String()) + + require.Equal(t, len(pipeSpec.receiverIDs), len(pipeline.receivers)) + require.Equal(t, len(pipeSpec.processorIDs), len(pipeline.processors)) + require.Equal(t, len(pipeSpec.exporterIDs), len(pipeline.exporters)) + + // The pipelineGraph is cumbersome to work with in this context because + // several type assertions & switches are necessary in order to access the + // validation functions included on the example components (Started, Stopped, etc) + // This gets a representation where all components are testcomponent.StatefulComponent + actualPipeline := pipeline.toStatefulComponentPipeline() + + for expNodeID := range pipeSpec.exporterIDs { + exp, ok := actualPipeline.exporters[expNodeID] + require.True(t, ok) + require.True(t, exp.Started()) + require.Equal(t, 0, len(exp.RecallTraces())) + require.Equal(t, 0, len(exp.RecallMetrics())) + require.Equal(t, 0, len(exp.RecallLogs())) + } + + for procNodeID := range pipeSpec.processorIDs { + proc, ok := actualPipeline.processors[procNodeID] + require.True(t, ok) + require.True(t, proc.Started()) + } + + for rcvrNodeID := range pipeSpec.receiverIDs { + rcvr, ok := actualPipeline.receivers[rcvrNodeID] + require.True(t, ok) + require.True(t, rcvr.Started()) + } + } + + // Push data into the pipelines. The list of receivers is retrieved directly from the overall + // component graph because we do not want to duplicate signal inputs to receivers that are + // shared between pipelines. The `allReceivers` function also excludes connectors, which we do + // not want to directly inject with signals. + allReceivers := allPipelines.getReceivers() + for _, rcvr := range allReceivers[component.DataTypeTraces] { + tracesReceiver := rcvr.(*testcomponents.ExampleReceiver) + assert.NoError(t, tracesReceiver.ConsumeTraces(context.Background(), testdata.GenerateTraces(1))) + } + for _, rcvr := range allReceivers[component.DataTypeMetrics] { + metricsReceiver := rcvr.(*testcomponents.ExampleReceiver) + assert.NoError(t, metricsReceiver.ConsumeMetrics(context.Background(), testdata.GenerateMetrics(1))) + } + for _, rcvr := range allReceivers[component.DataTypeLogs] { + logsReceiver := rcvr.(*testcomponents.ExampleReceiver) + assert.NoError(t, logsReceiver.ConsumeLogs(context.Background(), testdata.GenerateLogs(1))) + } + + // Shut down the entire component graph + assert.NoError(t, allPipelines.ShutdownAll(context.Background())) + + // Check each pipeline individually, ensuring that all components are stopped. + for pipelineID, pipeSpec := range test.pipelines.toMap() { + pipeline, ok := allPipelines.pipelineGraphs[pipelineID] + require.True(t, ok, "expected to find pipeline: %s", pipelineID.String()) + + actualPipeline := pipeline.toStatefulComponentPipeline() + + for rcvrNodeID := range pipeSpec.receiverIDs { + rcvr, ok := actualPipeline.receivers[rcvrNodeID] + require.True(t, ok) + require.True(t, rcvr.Stopped()) + } + + for procNodeID := range pipeSpec.processorIDs { + proc, ok := actualPipeline.processors[procNodeID] + require.True(t, ok) + require.True(t, proc.Stopped()) + } + + for expNodeID := range pipeSpec.exporterIDs { + exp, ok := actualPipeline.exporters[expNodeID] + require.True(t, ok) + require.True(t, exp.Stopped()) + } + } + + // Get the list of exporters directly from the overall component graph. Like receivers, + // exclude connectors and validate each exporter once regardless of sharing between pipelines. + allExporters := allPipelines.GetExporters() + for _, exp := range allExporters[component.DataTypeTraces] { + tracesExporter := exp.(*testcomponents.ExampleExporter) + assert.Equal(t, test.expectedPerExporter, len(tracesExporter.RecallTraces())) + for i := 0; i < test.expectedPerExporter; i++ { + assert.EqualValues(t, testdata.GenerateTraces(1), tracesExporter.RecallTraces()[0]) + } + } + for _, exp := range allExporters[component.DataTypeMetrics] { + metricsExporter := exp.(*testcomponents.ExampleExporter) + assert.Equal(t, test.expectedPerExporter, len(metricsExporter.RecallMetrics())) + for i := 0; i < test.expectedPerExporter; i++ { + assert.EqualValues(t, testdata.GenerateMetrics(1), metricsExporter.RecallMetrics()[0]) + } + } + for _, exp := range allExporters[component.DataTypeLogs] { + logsExporter := exp.(*testcomponents.ExampleExporter) + assert.Equal(t, test.expectedPerExporter, len(logsExporter.RecallLogs())) + for i := 0; i < test.expectedPerExporter; i++ { + assert.EqualValues(t, testdata.GenerateLogs(1), logsExporter.RecallLogs()[0]) + } + } + }) + } +} + +// This is a near-copy of the same test against the old struct. +// TODO this is superseded by the test above, but is included initially +// for direct comparison to prior implementation. +func TestNonConnectorPipelinesGraph(t *testing.T) { + tests := []struct { + name string + receiverIDs []component.ID + processorIDs []component.ID + exporterIDs []component.ID + expectedRequests int + }{ + { + name: "pipelines_simple.yaml", + receiverIDs: []component.ID{component.NewID("examplereceiver")}, + processorIDs: []component.ID{component.NewID("exampleprocessor")}, + exporterIDs: []component.ID{component.NewID("exampleexporter")}, + expectedRequests: 1, + }, + { + name: "pipelines_simple_multi_proc.yaml", + receiverIDs: []component.ID{component.NewID("examplereceiver")}, + processorIDs: []component.ID{component.NewID("exampleprocessor"), component.NewIDWithName("exampleprocessor", "1")}, + exporterIDs: []component.ID{component.NewID("exampleexporter")}, + expectedRequests: 1, + }, + { + name: "pipelines_simple_no_proc.yaml", + receiverIDs: []component.ID{component.NewID("examplereceiver")}, + exporterIDs: []component.ID{component.NewID("exampleexporter")}, + expectedRequests: 1, + }, + { + name: "pipelines_multi.yaml", + receiverIDs: []component.ID{component.NewID("examplereceiver"), component.NewIDWithName("examplereceiver", "1")}, + processorIDs: []component.ID{component.NewID("exampleprocessor"), component.NewIDWithName("exampleprocessor", "1")}, + exporterIDs: []component.ID{component.NewID("exampleexporter"), component.NewIDWithName("exampleexporter", "1")}, + expectedRequests: 2, + }, + { + name: "pipelines_multi_no_proc.yaml", + receiverIDs: []component.ID{component.NewID("examplereceiver"), component.NewIDWithName("examplereceiver", "1")}, + exporterIDs: []component.ID{component.NewID("exampleexporter"), component.NewIDWithName("exampleexporter", "1")}, + expectedRequests: 2, + }, + { + name: "pipelines_exporter_multi_pipeline.yaml", + receiverIDs: []component.ID{component.NewID("examplereceiver")}, + exporterIDs: []component.ID{component.NewID("exampleexporter")}, + expectedRequests: 2, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + factories, err := testcomponents.ExampleComponents() + assert.NoError(t, err) + + cfg := loadConfig(t, filepath.Join("testdata", test.name), factories) + + // Build the pipeline + pipelinesInterface, err := NewPipelinesGraph(context.Background(), toSettings(factories, cfg)) + assert.NoError(t, err) + + pipelines, ok := pipelinesInterface.(*pipelinesGraph) + require.True(t, ok) + + assert.NoError(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost())) + + // Verify exporters created, started and empty. + for _, expID := range test.exporterIDs { + traceExporter := pipelines.GetExporters()[component.DataTypeTraces][expID].(*testcomponents.ExampleExporter) + assert.True(t, traceExporter.Started()) + assert.Zero(t, len(traceExporter.RecallTraces())) + + // Validate metrics. + metricsExporter := pipelines.GetExporters()[component.DataTypeMetrics][expID].(*testcomponents.ExampleExporter) + assert.True(t, metricsExporter.Started()) + assert.Zero(t, len(metricsExporter.RecallMetrics())) + + // Validate logs. + logsExporter := pipelines.GetExporters()[component.DataTypeLogs][expID].(*testcomponents.ExampleExporter) + assert.True(t, logsExporter.Started()) + assert.Zero(t, len(logsExporter.RecallLogs())) + } + + // Verify processors created in the given order and started. + for i, procID := range test.processorIDs { + tracesNode := pipelines.pipelineGraphs[component.NewID(component.DataTypeTraces)].processors[i] + tracesProcessor := tracesNode.(*processorNode) + assert.Equal(t, procID, tracesProcessor.componentID) + assert.True(t, tracesProcessor.Component.(*testcomponents.ExampleProcessor).Started()) + + // Validate metrics. + metricsNode := pipelines.pipelineGraphs[component.NewID(component.DataTypeMetrics)].processors[i] + metricsProcessor := metricsNode.(*processorNode) + assert.Equal(t, procID, metricsProcessor.componentID) + assert.True(t, metricsProcessor.Component.(*testcomponents.ExampleProcessor).Started()) + + // Validate logs. + logsNode := pipelines.pipelineGraphs[component.NewID(component.DataTypeLogs)].processors[i] + logsProcessor := logsNode.(*processorNode) + assert.Equal(t, procID, logsProcessor.componentID) + assert.True(t, logsProcessor.Component.(*testcomponents.ExampleProcessor).Started()) + } + + // Verify receivers created, started and send data to confirm pipelines correctly connected. + for _, recvID := range test.receiverIDs { + traceReceiver := pipelines.getReceivers()[component.DataTypeTraces][recvID].(*testcomponents.ExampleReceiver) + assert.True(t, traceReceiver.Started()) + // Send traces. + assert.NoError(t, traceReceiver.ConsumeTraces(context.Background(), testdata.GenerateTraces(1))) + + metricsReceiver := pipelines.getReceivers()[component.DataTypeMetrics][recvID].(*testcomponents.ExampleReceiver) + assert.True(t, metricsReceiver.Started()) + // Send metrics. + assert.NoError(t, metricsReceiver.ConsumeMetrics(context.Background(), testdata.GenerateMetrics(1))) + + logsReceiver := pipelines.getReceivers()[component.DataTypeLogs][recvID].(*testcomponents.ExampleReceiver) + assert.True(t, logsReceiver.Started()) + // Send logs. + assert.NoError(t, logsReceiver.ConsumeLogs(context.Background(), testdata.GenerateLogs(1))) + } + + assert.NoError(t, pipelines.ShutdownAll(context.Background())) + + // Verify receivers shutdown. + for _, recvID := range test.receiverIDs { + traceReceiver := pipelines.getReceivers()[component.DataTypeTraces][recvID].(*testcomponents.ExampleReceiver) + assert.True(t, traceReceiver.Stopped()) + + metricsReceiver := pipelines.getReceivers()[component.DataTypeMetrics][recvID].(*testcomponents.ExampleReceiver) + assert.True(t, metricsReceiver.Stopped()) + + logsReceiver := pipelines.getReceivers()[component.DataTypeLogs][recvID].(*testcomponents.ExampleReceiver) + assert.True(t, logsReceiver.Stopped()) + } + + // Verify processors shutdown. + for i := range test.processorIDs { + traceNode := pipelines.pipelineGraphs[component.NewID(component.DataTypeTraces)].processors[i] + traceProcessor := traceNode.(*processorNode) + assert.True(t, traceProcessor.Component.(*testcomponents.ExampleProcessor).Stopped()) + + // Validate metrics. + metricsNode := pipelines.pipelineGraphs[component.NewID(component.DataTypeMetrics)].processors[i] + metricsProcessor := metricsNode.(*processorNode) + assert.True(t, metricsProcessor.Component.(*testcomponents.ExampleProcessor).Stopped()) + + // Validate logs. + logsNode := pipelines.pipelineGraphs[component.NewID(component.DataTypeLogs)].processors[i] + logsProcessor := logsNode.(*processorNode) + assert.True(t, logsProcessor.Component.(*testcomponents.ExampleProcessor).Stopped()) + } + + // Now verify that exporters received data, and are shutdown. + for _, expID := range test.exporterIDs { + // Validate traces. + traceExporter := pipelines.GetExporters()[component.DataTypeTraces][expID].(*testcomponents.ExampleExporter) + require.Len(t, traceExporter.RecallTraces(), test.expectedRequests) + assert.EqualValues(t, testdata.GenerateTraces(1), traceExporter.RecallTraces()[0]) + assert.True(t, traceExporter.Stopped()) + + // Validate metrics. + metricsExporter := pipelines.GetExporters()[component.DataTypeMetrics][expID].(*testcomponents.ExampleExporter) + require.Len(t, metricsExporter.RecallMetrics(), test.expectedRequests) + assert.EqualValues(t, testdata.GenerateMetrics(1), metricsExporter.RecallMetrics()[0]) + assert.True(t, metricsExporter.Stopped()) + + // Validate logs. + logsExporter := pipelines.GetExporters()[component.DataTypeLogs][expID].(*testcomponents.ExampleExporter) + require.Len(t, logsExporter.RecallLogs(), test.expectedRequests) + assert.EqualValues(t, testdata.GenerateLogs(1), logsExporter.RecallLogs()[0]) + assert.True(t, logsExporter.Stopped()) + } + }) + } +} + +// This includes all tests from the previous implmentation, plus many new ones +// relevant only to the new graph-based implementation. +func TestGraphBuildErrors(t *testing.T) { + nopReceiverFactory := componenttest.NewNopReceiverFactory() + nopProcessorFactory := componenttest.NewNopProcessorFactory() + nopExporterFactory := componenttest.NewNopExporterFactory() + nopConnectorFactory := componenttest.NewNopConnectorFactory() + badReceiverFactory := newBadReceiverFactory() + badProcessorFactory := newBadProcessorFactory() + badExporterFactory := newBadExporterFactory() + badConnectorFactory := newBadConnectorFactory() + + tests := []struct { + configFile string + }{ + {configFile: "not_supported_exporter_logs.yaml"}, + {configFile: "not_supported_exporter_metrics.yaml"}, + {configFile: "not_supported_exporter_traces.yaml"}, + {configFile: "not_supported_processor_logs.yaml"}, + {configFile: "not_supported_processor_metrics.yaml"}, + {configFile: "not_supported_processor_traces.yaml"}, + {configFile: "not_supported_receiver_traces.yaml"}, + {configFile: "not_supported_receiver_metrics.yaml"}, + {configFile: "not_supported_receiver_traces.yaml"}, + {configFile: "not_supported_connector_traces_traces.yaml"}, + {configFile: "not_supported_connector_traces_metrics.yaml"}, + {configFile: "not_supported_connector_traces_logs.yaml"}, + {configFile: "not_supported_connector_metrics_traces.yaml"}, + {configFile: "not_supported_connector_metrics_metrics.yaml"}, + {configFile: "not_supported_connector_metrics_logs.yaml"}, + {configFile: "not_supported_connector_logs_traces.yaml"}, + {configFile: "not_supported_connector_logs_metrics.yaml"}, + {configFile: "not_supported_connector_logs_logs.yaml"}, + {configFile: "not_allowed_conn_omit_recv_traces.yaml"}, + {configFile: "not_allowed_conn_omit_recv_metrics.yaml"}, + {configFile: "not_allowed_conn_omit_recv_logs.yaml"}, + {configFile: "not_allowed_conn_omit_exp_traces.yaml"}, + {configFile: "not_allowed_conn_omit_exp_metrics.yaml"}, + {configFile: "not_allowed_conn_omit_exp_logs.yaml"}, + {configFile: "not_allowed_simple_cycle_traces.yaml"}, + {configFile: "not_allowed_simple_cycle_metrics.yaml"}, + {configFile: "not_allowed_simple_cycle_logs.yaml"}, + {configFile: "not_allowed_deep_cycle_traces.yaml"}, + {configFile: "not_allowed_deep_cycle_metrics.yaml"}, + {configFile: "not_allowed_deep_cycle_logs.yaml"}, + {configFile: "not_allowed_deep_cycle_multi_signal.yaml"}, + {configFile: "unknown_exporter_config.yaml"}, + {configFile: "unknown_exporter_factory.yaml"}, + {configFile: "unknown_processor_config.yaml"}, + {configFile: "unknown_processor_factory.yaml"}, + {configFile: "unknown_receiver_config.yaml"}, + {configFile: "unknown_receiver_factory.yaml"}, + {configFile: "unknown_connector_config.yaml"}, + {configFile: "unknown_connector_factory.yaml"}, + } + + for _, test := range tests { + t.Run(test.configFile, func(t *testing.T) { + factories := component.Factories{ + Receivers: map[component.Type]component.ReceiverFactory{ + nopReceiverFactory.Type(): nopReceiverFactory, + "unknown": nopReceiverFactory, + badReceiverFactory.Type(): badReceiverFactory, + }, + Processors: map[component.Type]component.ProcessorFactory{ + nopProcessorFactory.Type(): nopProcessorFactory, + "unknown": nopProcessorFactory, + badProcessorFactory.Type(): badProcessorFactory, + }, + Exporters: map[component.Type]component.ExporterFactory{ + nopExporterFactory.Type(): nopExporterFactory, + "unknown": nopExporterFactory, + badExporterFactory.Type(): badExporterFactory, + }, + Connectors: map[component.Type]component.ConnectorFactory{ + nopConnectorFactory.Type(): nopConnectorFactory, + "unknown": nopConnectorFactory, + badConnectorFactory.Type(): badConnectorFactory, + }, + } + + // Need the unknown factories to do unmarshalling. + cfg := loadConfig(t, filepath.Join("testdata", test.configFile), factories) + + // Remove the unknown factories, so they are NOT available during building. + delete(factories.Exporters, "unknown") + delete(factories.Processors, "unknown") + delete(factories.Receivers, "unknown") + delete(factories.Connectors, "unknown") + + _, err := NewPipelinesGraph(context.Background(), toSettings(factories, cfg)) + assert.Error(t, err) + }) + } +} + +// This includes all tests from the previous implmentation, plus a new one +// relevant only to the new graph-based implementation. +func TestGraphFailToStartAndShutdown(t *testing.T) { + errReceiverFactory := newErrReceiverFactory() + errProcessorFactory := newErrProcessorFactory() + errExporterFactory := newErrExporterFactory() + errConnectorFactory := newErrConnectorFactory() + nopReceiverFactory := componenttest.NewNopReceiverFactory() + nopProcessorFactory := componenttest.NewNopProcessorFactory() + nopExporterFactory := componenttest.NewNopExporterFactory() + nopConnectorFactory := componenttest.NewNopConnectorFactory() + + set := Settings{ + Telemetry: componenttest.NewNopTelemetrySettings(), + BuildInfo: component.NewDefaultBuildInfo(), + ReceiverFactories: map[component.Type]component.ReceiverFactory{ + nopReceiverFactory.Type(): nopReceiverFactory, + errReceiverFactory.Type(): errReceiverFactory, + }, + ReceiverConfigs: map[component.ID]component.ReceiverConfig{ + component.NewID(nopReceiverFactory.Type()): nopReceiverFactory.CreateDefaultConfig(), + component.NewID(errReceiverFactory.Type()): errReceiverFactory.CreateDefaultConfig(), + }, + ProcessorFactories: map[component.Type]component.ProcessorFactory{ + nopProcessorFactory.Type(): nopProcessorFactory, + errProcessorFactory.Type(): errProcessorFactory, + }, + ProcessorConfigs: map[component.ID]component.ProcessorConfig{ + component.NewID(nopProcessorFactory.Type()): nopProcessorFactory.CreateDefaultConfig(), + component.NewID(errProcessorFactory.Type()): errProcessorFactory.CreateDefaultConfig(), + }, + ExporterFactories: map[component.Type]component.ExporterFactory{ + nopExporterFactory.Type(): nopExporterFactory, + errExporterFactory.Type(): errExporterFactory, + }, + ExporterConfigs: map[component.ID]component.ExporterConfig{ + component.NewID(nopExporterFactory.Type()): nopExporterFactory.CreateDefaultConfig(), + component.NewID(errExporterFactory.Type()): errExporterFactory.CreateDefaultConfig(), + }, + ConnectorFactories: map[component.Type]component.ConnectorFactory{ + nopConnectorFactory.Type(): nopConnectorFactory, + errConnectorFactory.Type(): errConnectorFactory, + }, + ConnectorConfigs: map[component.ID]component.ConnectorConfig{ + component.NewIDWithName(nopConnectorFactory.Type(), "conn"): nopConnectorFactory.CreateDefaultConfig(), + component.NewIDWithName(errConnectorFactory.Type(), "conn"): errConnectorFactory.CreateDefaultConfig(), + }, + } + + dataTypes := []component.DataType{component.DataTypeTraces, component.DataTypeMetrics, component.DataTypeLogs} + for _, dt := range dataTypes { + t.Run(string(dt)+"/receiver", func(t *testing.T) { + set.PipelineConfigs = map[component.ID]*config.Pipeline{ + component.NewID(dt): { + Receivers: []component.ID{component.NewID("nop"), component.NewID("err")}, + Processors: []component.ID{component.NewID("nop")}, + Exporters: []component.ID{component.NewID("nop")}, + }, + } + pipelines, err := NewPipelinesGraph(context.Background(), set) + assert.NoError(t, err) + assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost())) + assert.Error(t, pipelines.ShutdownAll(context.Background())) + }) + + t.Run(string(dt)+"/processor", func(t *testing.T) { + set.PipelineConfigs = map[component.ID]*config.Pipeline{ + component.NewID(dt): { + Receivers: []component.ID{component.NewID("nop")}, + Processors: []component.ID{component.NewID("nop"), component.NewID("err")}, + Exporters: []component.ID{component.NewID("nop")}, + }, + } + pipelines, err := NewPipelinesGraph(context.Background(), set) + assert.NoError(t, err) + assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost())) + assert.Error(t, pipelines.ShutdownAll(context.Background())) + }) + + t.Run(string(dt)+"/exporter", func(t *testing.T) { + set.PipelineConfigs = map[component.ID]*config.Pipeline{ + component.NewID(dt): { + Receivers: []component.ID{component.NewID("nop")}, + Processors: []component.ID{component.NewID("nop")}, + Exporters: []component.ID{component.NewID("nop"), component.NewID("err")}, + }, + } + pipelines, err := NewPipelinesGraph(context.Background(), set) + assert.NoError(t, err) + assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost())) + assert.Error(t, pipelines.ShutdownAll(context.Background())) + }) + + for _, dt2 := range dataTypes { + t.Run(string(dt)+"/"+string(dt2)+"/connector", func(t *testing.T) { + set.PipelineConfigs = map[component.ID]*config.Pipeline{ + component.NewIDWithName(dt, "in"): { + Receivers: []component.ID{component.NewID("nop")}, + Processors: []component.ID{component.NewID("nop")}, + Exporters: []component.ID{component.NewID("nop"), component.NewIDWithName("err", "conn")}, + }, + component.NewIDWithName(dt2, "out"): { + Receivers: []component.ID{component.NewID("nop"), component.NewIDWithName("err", "conn")}, + Processors: []component.ID{component.NewID("nop")}, + Exporters: []component.ID{component.NewID("nop")}, + }, + } + pipelines, err := NewPipelinesGraph(context.Background(), set) + assert.NoError(t, err) + assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost())) + assert.Error(t, pipelines.ShutdownAll(context.Background())) + }) + } + } +} + +func (g *pipelinesGraph) getReceivers() map[component.DataType]map[component.ID]component.Component { + receiversMap := make(map[component.DataType]map[component.ID]component.Component) + receiversMap[component.DataTypeTraces] = make(map[component.ID]component.Component) + receiversMap[component.DataTypeMetrics] = make(map[component.ID]component.Component) + receiversMap[component.DataTypeLogs] = make(map[component.ID]component.Component) + + for _, pg := range g.pipelineGraphs { + for _, rcvrNode := range pg.receivers { + rcvrOrConnNode := g.componentGraph.Node(rcvrNode.ID()) + rcvrNode, ok := rcvrOrConnNode.(*receiverNode) + if !ok { + continue + } + receiversMap[rcvrNode.pipelineType][rcvrNode.componentID] = rcvrNode.Component + } + } + return receiversMap +} diff --git a/service/internal/pipelines/nodes.go b/service/internal/pipelines/nodes.go new file mode 100644 index 000000000000..4bc300559411 --- /dev/null +++ b/service/internal/pipelines/nodes.go @@ -0,0 +1,491 @@ +// 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 pipelines // import "go.opentelemetry.io/collector/service/internal/pipelines" + +import ( + "context" + "fmt" + "hash/fnv" + + "gonum.org/v1/gonum/graph" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/service/internal/components" + "go.opentelemetry.io/collector/service/internal/fanoutconsumer" +) + +type componentNodeID int64 + +func (n componentNodeID) ID() int64 { + return int64(n) +} + +func newComponentNodeID(parts ...string) componentNodeID { + h := fnv.New64a() + for _, part := range parts { + _, _ = h.Write([]byte(part)) + } + return componentNodeID(int64(h.Sum64())) +} + +type componentNode interface { + ComponentID() component.ID + component.Component + graph.Node +} + +var _ componentNode = &receiverNode{} + +// A receiver instance can be shared by multiple pipelines of the same type. +// Therefore, componentNodeID is derived from "pipeline type" and "component ID". +type receiverNode struct { + componentNodeID + componentID component.ID + pipelineType component.DataType + cfg component.ReceiverConfig + factory component.ReceiverFactory + component.Component +} + +func newReceiverNodeID(pipelineType component.DataType, recvID component.ID) componentNodeID { + return newComponentNodeID("receiver", string(pipelineType), recvID.String()) +} + +func (n *receiverNode) ComponentID() component.ID { + return n.componentID +} + +func (n *receiverNode) build( + ctx context.Context, + tel component.TelemetrySettings, + info component.BuildInfo, + nexts []baseConsumer, +) error { + set := component.ReceiverCreateSettings{TelemetrySettings: tel, BuildInfo: info} + set.TelemetrySettings.Logger = receiverLogger(set.TelemetrySettings.Logger, n.componentID, n.pipelineType) + components.LogStabilityLevel(set.TelemetrySettings.Logger, getReceiverStabilityLevel(n.factory, n.pipelineType)) + + var err error + switch n.pipelineType { + case component.DataTypeTraces: + var consumers []consumer.Traces + for _, next := range nexts { + tracesConsumer, ok := next.(consumer.Traces) + if !ok { + return fmt.Errorf("next component is not a traces consumer: %s", n.componentID) + } + consumers = append(consumers, tracesConsumer) + } + n.Component, err = n.factory.CreateTracesReceiver(ctx, set, n.cfg, fanoutconsumer.NewTraces(consumers)) + if err != nil { + return err + } + case component.DataTypeMetrics: + var consumers []consumer.Metrics + for _, next := range nexts { + metricsConsumer, ok := next.(consumer.Metrics) + if !ok { + return fmt.Errorf("next component is not a metrics consumer: %s", n.componentID) + } + consumers = append(consumers, metricsConsumer) + } + n.Component, err = n.factory.CreateMetricsReceiver(ctx, set, n.cfg, fanoutconsumer.NewMetrics(consumers)) + if err != nil { + return err + } + case component.DataTypeLogs: + var consumers []consumer.Logs + for _, next := range nexts { + logsConsumer, ok := next.(consumer.Logs) + if !ok { + return fmt.Errorf("next component is not a logs consumer: %s", n.componentID) + } + consumers = append(consumers, logsConsumer) + } + n.Component, err = n.factory.CreateLogsReceiver(ctx, set, n.cfg, fanoutconsumer.NewLogs(consumers)) + if err != nil { + return err + } + default: + return fmt.Errorf("error creating receiver %q, data type %q is not supported", n.componentID, n.pipelineType) + } + return nil +} + +var _ componentNode = &processorNode{} + +// Every processor instance is unique to one pipeline. +// Therefore, componentNodeID is derived from "pipeline ID" and "component ID". +type processorNode struct { + componentNodeID + componentID component.ID + pipelineID component.ID + cfg component.ProcessorConfig + factory component.ProcessorFactory + component.Component +} + +func (n *processorNode) ComponentID() component.ID { + return n.componentID +} + +func newProcessorNodeID(pipelineID, procID component.ID) componentNodeID { + return newComponentNodeID("processor", pipelineID.String(), procID.String()) +} + +func (n *processorNode) build( + ctx context.Context, + tel component.TelemetrySettings, + info component.BuildInfo, + next baseConsumer, +) error { + set := component.ProcessorCreateSettings{TelemetrySettings: tel, BuildInfo: info} + set.TelemetrySettings.Logger = processorLogger(set.TelemetrySettings.Logger, n.componentID, n.pipelineID) + components.LogStabilityLevel(set.TelemetrySettings.Logger, getProcessorStabilityLevel(n.factory, n.pipelineID.Type())) + + var err error + switch n.pipelineID.Type() { + case component.DataTypeTraces: + tracesConsumer, ok := next.(consumer.Traces) + if !ok { + return fmt.Errorf("next component is not a traces consumer: %s", n.componentID) + } + n.Component, err = n.factory.CreateTracesProcessor(ctx, set, n.cfg, tracesConsumer) + if err != nil { + return err + } + case component.DataTypeMetrics: + metricsConsumer, ok := next.(consumer.Metrics) + if !ok { + return fmt.Errorf("next component is not a metrics consumer: %s", n.componentID) + } + n.Component, err = n.factory.CreateMetricsProcessor(ctx, set, n.cfg, metricsConsumer) + if err != nil { + return err + } + case component.DataTypeLogs: + logsConsumer, ok := next.(consumer.Logs) + if !ok { + return fmt.Errorf("next component is not a logs consumer: %s", n.componentID) + } + n.Component, err = n.factory.CreateLogsProcessor(ctx, set, n.cfg, logsConsumer) + if err != nil { + return err + } + default: + return fmt.Errorf("error creating processor %q, data type %q is not supported", n.componentID, n.pipelineID.Type()) + } + return nil +} + +var _ componentNode = &exporterNode{} + +// An exporter instance can be shared by multiple pipelines of the same type. +// Therefore, componentNodeID is derived from "pipeline type" and "component ID". +type exporterNode struct { + componentNodeID + componentID component.ID + pipelineType component.DataType + cfg component.ExporterConfig + factory component.ExporterFactory + component.Component +} + +func (n *exporterNode) ComponentID() component.ID { + return n.componentID +} + +func newExporterNodeID(pipelineType component.DataType, exprID component.ID) componentNodeID { + return newComponentNodeID("exporter", string(pipelineType), exprID.String()) +} + +func (n *exporterNode) build( + ctx context.Context, + tel component.TelemetrySettings, + info component.BuildInfo, +) error { + set := component.ExporterCreateSettings{TelemetrySettings: tel, BuildInfo: info} + set.TelemetrySettings.Logger = exporterLogger(set.TelemetrySettings.Logger, n.componentID, n.pipelineType) + components.LogStabilityLevel(set.TelemetrySettings.Logger, getExporterStabilityLevel(n.factory, n.pipelineType)) + + var err error + switch n.pipelineType { + case component.DataTypeTraces: + n.Component, err = n.factory.CreateTracesExporter(ctx, set, n.cfg) + if err != nil { + return err + } + case component.DataTypeMetrics: + n.Component, err = n.factory.CreateMetricsExporter(ctx, set, n.cfg) + if err != nil { + return err + } + fmt.Printf("built exporter: %v", n.Component) + case component.DataTypeLogs: + n.Component, err = n.factory.CreateLogsExporter(ctx, set, n.cfg) + if err != nil { + return err + } + default: + return fmt.Errorf("error creating exporter %q, data type %q is not supported", n.componentID, n.pipelineType) + } + return nil +} + +var _ componentNode = &connectorNode{} + +// A connector instance connects one pipeline type to one other pipeline type. +// Therefore, componentNodeID is derived from "exporter pipeline type", "receiver pipeline type", and "component ID". +type connectorNode struct { + componentNodeID + componentID component.ID + exprPipelineType component.DataType + rcvrPipelineType component.DataType + cfg component.ConnectorConfig + factory component.ConnectorFactory + component.Component +} + +func (n *connectorNode) ComponentID() component.ID { + return n.componentID +} + +type connectorConsumerTraces struct { + component.Component + consumer.Traces +} +type connectorConsumerMetrics struct { + component.Component + consumer.Metrics +} +type connectorConsumerLogs struct { + component.Component + consumer.Logs +} + +func newConnectorNodeID(exprPipelineType, rcvrPipelineType component.DataType, connID component.ID) componentNodeID { + return newComponentNodeID("connector", connID.String(), string(exprPipelineType), string(rcvrPipelineType)) +} + +func (n *connectorNode) build( + ctx context.Context, + tel component.TelemetrySettings, + info component.BuildInfo, + nexts []baseConsumer, +) error { + set := component.ConnectorCreateSettings{TelemetrySettings: tel, BuildInfo: info} + set.TelemetrySettings.Logger = connectorLogger(set.TelemetrySettings.Logger, n.componentID, n.exprPipelineType, n.rcvrPipelineType) + components.LogStabilityLevel(set.TelemetrySettings.Logger, getConnectorStabilityLevel(n.factory, n.exprPipelineType, n.rcvrPipelineType)) + + switch n.rcvrPipelineType { + case component.DataTypeTraces: + var consumers []consumer.Traces + for _, next := range nexts { + tracesConsumer, ok := next.(consumer.Traces) + if !ok { + return fmt.Errorf("next component is not a traces consumer: %s", n.componentID) + } + consumers = append(consumers, tracesConsumer) + } + fanoutConsumer := fanoutconsumer.NewTraces(consumers) + switch n.exprPipelineType { + case component.DataTypeTraces: + conn, err := n.factory.CreateTracesToTracesConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewTraces(conn.ConsumeTracesToTraces) + if consErr != nil { + return consErr + } + n.Component = connectorConsumerTraces{Component: conn, Traces: cons} + case component.DataTypeMetrics: + conn, err := n.factory.CreateMetricsToTracesConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewMetrics(conn.ConsumeMetricsToTraces) + if consErr != nil { + return consErr + } + n.Component = connectorConsumerMetrics{Component: conn, Metrics: cons} + case component.DataTypeLogs: + conn, err := n.factory.CreateLogsToTracesConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewLogs(conn.ConsumeLogsToTraces) + if consErr != nil { + return err + } + n.Component = connectorConsumerLogs{Component: conn, Logs: cons} + } + case component.DataTypeMetrics: + var consumers []consumer.Metrics + for _, next := range nexts { + metricsConsumer, ok := next.(consumer.Metrics) + if !ok { + return fmt.Errorf("next component is not a metrics consumer: %s", n.componentID) + } + consumers = append(consumers, metricsConsumer) + } + fanoutConsumer := fanoutconsumer.NewMetrics(consumers) + switch n.exprPipelineType { + case component.DataTypeTraces: + conn, err := n.factory.CreateTracesToMetricsConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewTraces(conn.ConsumeTracesToMetrics) + if consErr != nil { + return err + } + n.Component = connectorConsumerTraces{Component: conn, Traces: cons} + case component.DataTypeMetrics: + conn, err := n.factory.CreateMetricsToMetricsConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewMetrics(conn.ConsumeMetricsToMetrics) + if consErr != nil { + return err + } + n.Component = connectorConsumerMetrics{Component: conn, Metrics: cons} + case component.DataTypeLogs: + conn, err := n.factory.CreateLogsToMetricsConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewLogs(conn.ConsumeLogsToMetrics) + if consErr != nil { + return err + } + n.Component = connectorConsumerLogs{Component: conn, Logs: cons} + } + case component.DataTypeLogs: + var consumers []consumer.Logs + for _, next := range nexts { + logsConsumer, ok := next.(consumer.Logs) + if !ok { + return fmt.Errorf("next component is not a logs consumer: %s", n.componentID) + } + consumers = append(consumers, logsConsumer) + } + fanoutConsumer := fanoutconsumer.NewLogs(consumers) + switch n.exprPipelineType { + case component.DataTypeTraces: + conn, err := n.factory.CreateTracesToLogsConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewTraces(conn.ConsumeTracesToLogs) + if consErr != nil { + return err + } + n.Component = connectorConsumerTraces{Component: conn, Traces: cons} + case component.DataTypeMetrics: + conn, err := n.factory.CreateMetricsToLogsConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewMetrics(conn.ConsumeMetricsToLogs) + if consErr != nil { + return err + } + n.Component = connectorConsumerMetrics{Component: conn, Metrics: cons} + case component.DataTypeLogs: + conn, err := n.factory.CreateLogsToLogsConnector(ctx, set, n.cfg, fanoutConsumer) + if err != nil { + return err + } + cons, consErr := consumer.NewLogs(conn.ConsumeLogsToLogs) + if consErr != nil { + return consErr + } + n.Component = connectorConsumerLogs{Component: conn, Logs: cons} + } + } + return nil +} + +// If a pipeline has any processors, a fan-in is added before the first one. +// The main purpose of this node is to present aggregated capabilities to receivers, +// such as whether the pipeline mutates data. +// The componentNodeID is derived from "pipeline ID". +type fanInNode struct { + componentNodeID + pipelineID component.ID + baseConsumer + consumer.Capabilities +} + +func newFanInNode(pipelineID component.ID) *fanInNode { + return &fanInNode{ + componentNodeID: newComponentNodeID("fanin_to_processors", pipelineID.String()), + pipelineID: pipelineID, + Capabilities: consumer.Capabilities{}, + } +} + +func (n *fanInNode) build(nextConsumer baseConsumer, processors []*processorNode) { + n.baseConsumer = nextConsumer + for _, proc := range processors { + n.Capabilities.MutatesData = n.Capabilities.MutatesData || + proc.Component.(baseConsumer).Capabilities().MutatesData + } +} + +// Each pipeline has one fan-out node before exporters. +// Therefore, componentNodeID is derived from "pipeline ID". +type fanOutNode struct { + componentNodeID + pipelineID component.ID + baseConsumer +} + +func newFanOutNode(pipelineID component.ID) *fanOutNode { + return &fanOutNode{ + componentNodeID: newComponentNodeID("fanout_to_exporters", pipelineID.String()), + pipelineID: pipelineID, + } +} + +func (n *fanOutNode) build(nextConsumers []baseConsumer) error { + switch n.pipelineID.Type() { + case component.DataTypeTraces: + consumers := make([]consumer.Traces, 0, len(nextConsumers)) + for _, next := range nextConsumers { + consumers = append(consumers, next.(consumer.Traces)) + } + n.baseConsumer = fanoutconsumer.NewTraces(consumers) + case component.DataTypeMetrics: + consumers := make([]consumer.Metrics, 0, len(nextConsumers)) + for _, next := range nextConsumers { + + consumers = append(consumers, next.(consumer.Metrics)) + } + n.baseConsumer = fanoutconsumer.NewMetrics(consumers) + case component.DataTypeLogs: + consumers := make([]consumer.Logs, 0, len(nextConsumers)) + for _, next := range nextConsumers { + consumers = append(consumers, next.(consumer.Logs)) + } + n.baseConsumer = fanoutconsumer.NewLogs(consumers) + default: + return fmt.Errorf("create fan-out exporter in pipeline %q, data type %q is not supported", n.pipelineID, n.pipelineID.Type()) + } + return nil +} diff --git a/service/internal/pipelines/pipelines.go b/service/internal/pipelines/pipelines.go index 726ca95e6fa2..880e1c5e0007 100644 --- a/service/internal/pipelines/pipelines.go +++ b/service/internal/pipelines/pipelines.go @@ -55,8 +55,15 @@ type builtPipeline struct { exporters []builtComponent } -// Pipelines is set of all pipelines created from exporter configs. -type Pipelines struct { +type Pipelines interface { + StartAll(ctx context.Context, host component.Host) error + ShutdownAll(ctx context.Context) error + GetExporters() map[component.DataType]map[component.ID]component.Component + HandleZPages(w http.ResponseWriter, r *http.Request) +} + +// traditionalPipelines is set of all pipelines created from exporter configs. +type traditionalPipelines struct { telemetry component.TelemetrySettings allReceivers map[component.DataType]map[component.ID]component.Component @@ -70,7 +77,7 @@ type Pipelines struct { // Start with exporters, processors (in reverse configured order), then receivers. // This is important so that components that are earlier in the pipeline and reference components that are // later in the pipeline do not start sending data to later components which are not yet started. -func (bps *Pipelines) StartAll(ctx context.Context, host component.Host) error { +func (bps *traditionalPipelines) StartAll(ctx context.Context, host component.Host) error { bps.telemetry.Logger.Info("Starting exporters...") for dt, expByID := range bps.allExporters { for expID, exp := range expByID { @@ -113,7 +120,7 @@ func (bps *Pipelines) StartAll(ctx context.Context, host component.Host) error { // // Shutdown order is the reverse of starting: receivers, processors, then exporters. // This gives senders a chance to send all their data to a not "shutdown" component. -func (bps *Pipelines) ShutdownAll(ctx context.Context) error { +func (bps *traditionalPipelines) ShutdownAll(ctx context.Context) error { var errs error bps.telemetry.Logger.Info("Stopping receivers...") for _, recvByID := range bps.allReceivers { @@ -139,9 +146,8 @@ func (bps *Pipelines) ShutdownAll(ctx context.Context) error { return errs } -func (bps *Pipelines) GetExporters() map[component.DataType]map[component.ID]component.Component { +func (bps *traditionalPipelines) GetExporters() map[component.DataType]map[component.ID]component.Component { exportersMap := make(map[component.DataType]map[component.ID]component.Component) - exportersMap[component.DataTypeTraces] = make(map[component.ID]component.Component, len(bps.allExporters[component.DataTypeTraces])) exportersMap[component.DataTypeMetrics] = make(map[component.ID]component.Component, len(bps.allExporters[component.DataTypeMetrics])) exportersMap[component.DataTypeLogs] = make(map[component.ID]component.Component, len(bps.allExporters[component.DataTypeLogs])) @@ -155,7 +161,7 @@ func (bps *Pipelines) GetExporters() map[component.DataType]map[component.ID]com return exportersMap } -func (bps *Pipelines) HandleZPages(w http.ResponseWriter, r *http.Request) { +func (bps *traditionalPipelines) HandleZPages(w http.ResponseWriter, r *http.Request) { qValues := r.URL.Query() pipelineName := qValues.Get(zPipelineName) componentName := qValues.Get(zComponentName) @@ -200,13 +206,19 @@ type Settings struct { // ExporterConfigs is a map of component.ID to component.ExporterConfig. ExporterConfigs map[component.ID]component.ExporterConfig + // ConnectorFactories maps exporter type names in the config to the respective component.ConnectorFactory. + ConnectorFactories map[component.Type]component.ConnectorFactory + + // ConnectorConfigs is a map of component.ID to component.ConnectorConfig. + ConnectorConfigs map[component.ID]component.ConnectorConfig + // PipelineConfigs is a map of component.ID to config.Pipeline. PipelineConfigs map[component.ID]*config.Pipeline } // Build builds all pipelines from config. -func Build(ctx context.Context, set Settings) (*Pipelines, error) { - exps := &Pipelines{ +func Build(ctx context.Context, set Settings) (Pipelines, error) { + exps := &traditionalPipelines{ telemetry: set.Telemetry, allReceivers: make(map[component.DataType]map[component.ID]component.Component), allExporters: make(map[component.DataType]map[component.ID]component.Component), @@ -568,7 +580,7 @@ func getReceiverStabilityLevel(factory component.ReceiverFactory, dt component.D return component.StabilityLevelUndefined } -func (bps *Pipelines) getPipelinesSummaryTableData() zpages.SummaryPipelinesTableData { +func (bps *traditionalPipelines) getPipelinesSummaryTableData() zpages.SummaryPipelinesTableData { sumData := zpages.SummaryPipelinesTableData{} sumData.Rows = make([]zpages.SummaryPipelinesTableRowData, 0, len(bps.pipelines)) for c, p := range bps.pipelines { @@ -601,3 +613,44 @@ func (bps *Pipelines) getPipelinesSummaryTableData() zpages.SummaryPipelinesTabl }) return sumData } + +func connectorLogger(logger *zap.Logger, connID component.ID, expPipelineType, rcvrPipelineType component.DataType) *zap.Logger { + return logger.With( + zap.String(components.ZapKindKey, components.ZapKindExporter), + zap.String(components.ZapNameKey, connID.String()), + zap.String(components.ZapRoleExporterInPipeline, string(expPipelineType)), + zap.String(components.ZapRoleReceiverInPipeline, string(rcvrPipelineType))) +} + +func getConnectorStabilityLevel(factory component.ConnectorFactory, edt, rdt component.DataType) component.StabilityLevel { + switch edt { + case component.DataTypeTraces: + switch rdt { + case component.DataTypeTraces: + return factory.TracesToTracesConnectorStability() + case component.DataTypeMetrics: + return factory.TracesToMetricsConnectorStability() + case component.DataTypeLogs: + return factory.TracesToLogsConnectorStability() + } + case component.DataTypeMetrics: + switch rdt { + case component.DataTypeTraces: + return factory.MetricsToTracesConnectorStability() + case component.DataTypeMetrics: + return factory.MetricsToMetricsConnectorStability() + case component.DataTypeLogs: + return factory.MetricsToLogsConnectorStability() + } + case component.DataTypeLogs: + switch rdt { + case component.DataTypeTraces: + return factory.LogsToTracesConnectorStability() + case component.DataTypeMetrics: + return factory.LogsToMetricsConnectorStability() + case component.DataTypeLogs: + return factory.LogsToLogsConnectorStability() + } + } + return component.StabilityLevelUndefined +} diff --git a/service/internal/pipelines/pipelines_test.go b/service/internal/pipelines/pipelines_test.go index 2802d68dbf52..f6b519426972 100644 --- a/service/internal/pipelines/pipelines_test.go +++ b/service/internal/pipelines/pipelines_test.go @@ -31,6 +31,9 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/internal/testdata" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/service/internal/configunmarshaler" "go.opentelemetry.io/collector/service/internal/testcomponents" ) @@ -92,59 +95,62 @@ func TestBuild(t *testing.T) { cfg := loadConfig(t, filepath.Join("testdata", test.name), factories) // Build the pipeline - pipelines, err := Build(context.Background(), toSettings(factories, cfg)) + pipelinesInterface, err := Build(context.Background(), toSettings(factories, cfg)) assert.NoError(t, err) + pipelines, ok := pipelinesInterface.(*traditionalPipelines) + require.True(t, ok) + assert.NoError(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost())) // Verify exporters created, started and empty. for _, expID := range test.exporterIDs { traceExporter := pipelines.GetExporters()[component.DataTypeTraces][expID].(*testcomponents.ExampleExporter) - assert.True(t, traceExporter.Started) - assert.Equal(t, len(traceExporter.Traces), 0) + assert.True(t, traceExporter.Started()) + assert.Zero(t, len(traceExporter.RecallTraces())) // Validate metrics. metricsExporter := pipelines.GetExporters()[component.DataTypeMetrics][expID].(*testcomponents.ExampleExporter) - assert.True(t, metricsExporter.Started) - assert.Zero(t, len(metricsExporter.Traces)) + assert.True(t, metricsExporter.Started()) + assert.Zero(t, len(metricsExporter.RecallMetrics())) // Validate logs. logsExporter := pipelines.GetExporters()[component.DataTypeLogs][expID].(*testcomponents.ExampleExporter) - assert.True(t, logsExporter.Started) - assert.Zero(t, len(logsExporter.Traces)) + assert.True(t, logsExporter.Started()) + assert.Zero(t, len(logsExporter.RecallLogs())) } // Verify processors created in the given order and started. for i, procID := range test.processorIDs { traceProcessor := pipelines.pipelines[component.NewID(component.DataTypeTraces)].processors[i] assert.Equal(t, procID, traceProcessor.id) - assert.True(t, traceProcessor.comp.(*testcomponents.ExampleProcessor).Started) + assert.True(t, traceProcessor.comp.(*testcomponents.ExampleProcessor).Started()) // Validate metrics. metricsProcessor := pipelines.pipelines[component.NewID(component.DataTypeMetrics)].processors[i] assert.Equal(t, procID, metricsProcessor.id) - assert.True(t, metricsProcessor.comp.(*testcomponents.ExampleProcessor).Started) + assert.True(t, metricsProcessor.comp.(*testcomponents.ExampleProcessor).Started()) // Validate logs. logsProcessor := pipelines.pipelines[component.NewID(component.DataTypeLogs)].processors[i] assert.Equal(t, procID, logsProcessor.id) - assert.True(t, logsProcessor.comp.(*testcomponents.ExampleProcessor).Started) + assert.True(t, logsProcessor.comp.(*testcomponents.ExampleProcessor).Started()) } // Verify receivers created, started and send data to confirm pipelines correctly connected. for _, recvID := range test.receiverIDs { traceReceiver := pipelines.allReceivers[component.DataTypeTraces][recvID].(*testcomponents.ExampleReceiver) - assert.True(t, traceReceiver.Started) + assert.True(t, traceReceiver.Started()) // Send traces. assert.NoError(t, traceReceiver.ConsumeTraces(context.Background(), testdata.GenerateTraces(1))) metricsReceiver := pipelines.allReceivers[component.DataTypeMetrics][recvID].(*testcomponents.ExampleReceiver) - assert.True(t, metricsReceiver.Started) + assert.True(t, metricsReceiver.Started()) // Send metrics. assert.NoError(t, metricsReceiver.ConsumeMetrics(context.Background(), testdata.GenerateMetrics(1))) logsReceiver := pipelines.allReceivers[component.DataTypeLogs][recvID].(*testcomponents.ExampleReceiver) - assert.True(t, logsReceiver.Started) + assert.True(t, logsReceiver.Started()) // Send logs. assert.NoError(t, logsReceiver.ConsumeLogs(context.Background(), testdata.GenerateLogs(1))) } @@ -154,48 +160,48 @@ func TestBuild(t *testing.T) { // Verify receivers shutdown. for _, recvID := range test.receiverIDs { traceReceiver := pipelines.allReceivers[component.DataTypeTraces][recvID].(*testcomponents.ExampleReceiver) - assert.True(t, traceReceiver.Stopped) + assert.True(t, traceReceiver.Stopped()) metricsReceiver := pipelines.allReceivers[component.DataTypeMetrics][recvID].(*testcomponents.ExampleReceiver) - assert.True(t, metricsReceiver.Stopped) + assert.True(t, metricsReceiver.Stopped()) logsReceiver := pipelines.allReceivers[component.DataTypeLogs][recvID].(*testcomponents.ExampleReceiver) - assert.True(t, logsReceiver.Stopped) + assert.True(t, logsReceiver.Stopped()) } // Verify processors shutdown. for i := range test.processorIDs { traceProcessor := pipelines.pipelines[component.NewID(component.DataTypeTraces)].processors[i] - assert.True(t, traceProcessor.comp.(*testcomponents.ExampleProcessor).Stopped) + assert.True(t, traceProcessor.comp.(*testcomponents.ExampleProcessor).Stopped()) // Validate metrics. metricsProcessor := pipelines.pipelines[component.NewID(component.DataTypeMetrics)].processors[i] - assert.True(t, metricsProcessor.comp.(*testcomponents.ExampleProcessor).Stopped) + assert.True(t, metricsProcessor.comp.(*testcomponents.ExampleProcessor).Stopped()) // Validate logs. logsProcessor := pipelines.pipelines[component.NewID(component.DataTypeLogs)].processors[i] - assert.True(t, logsProcessor.comp.(*testcomponents.ExampleProcessor).Stopped) + assert.True(t, logsProcessor.comp.(*testcomponents.ExampleProcessor).Stopped()) } // Now verify that exporters received data, and are shutdown. for _, expID := range test.exporterIDs { // Validate traces. traceExporter := pipelines.GetExporters()[component.DataTypeTraces][expID].(*testcomponents.ExampleExporter) - require.Len(t, traceExporter.Traces, test.expectedRequests) - assert.EqualValues(t, testdata.GenerateTraces(1), traceExporter.Traces[0]) - assert.True(t, traceExporter.Stopped) + require.Len(t, traceExporter.RecallTraces(), test.expectedRequests) + assert.EqualValues(t, testdata.GenerateTraces(1), traceExporter.RecallTraces()[0]) + assert.True(t, traceExporter.Stopped()) // Validate metrics. metricsExporter := pipelines.GetExporters()[component.DataTypeMetrics][expID].(*testcomponents.ExampleExporter) - require.Len(t, metricsExporter.Metrics, test.expectedRequests) - assert.EqualValues(t, testdata.GenerateMetrics(1), metricsExporter.Metrics[0]) - assert.True(t, metricsExporter.Stopped) + require.Len(t, metricsExporter.RecallMetrics(), test.expectedRequests) + assert.EqualValues(t, testdata.GenerateMetrics(1), metricsExporter.RecallMetrics()[0]) + assert.True(t, metricsExporter.Stopped()) // Validate logs. logsExporter := pipelines.GetExporters()[component.DataTypeLogs][expID].(*testcomponents.ExampleExporter) - require.Len(t, logsExporter.Logs, test.expectedRequests) - assert.EqualValues(t, testdata.GenerateLogs(1), logsExporter.Logs[0]) - assert.True(t, logsExporter.Stopped) + require.Len(t, logsExporter.RecallLogs(), test.expectedRequests) + assert.EqualValues(t, testdata.GenerateLogs(1), logsExporter.RecallLogs()[0]) + assert.True(t, logsExporter.Stopped()) } }) } @@ -375,6 +381,16 @@ func newBadExporterFactory() component.ExporterFactory { }) } +func newBadConnectorFactory() component.ConnectorFactory { + return component.NewConnectorFactory("bf", func() component.ConnectorConfig { + return &struct { + config.ConnectorSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct + }{ + ConnectorSettings: config.NewConnectorSettings(component.NewID("bf")), + } + }) +} + func newErrReceiverFactory() component.ReceiverFactory { return component.NewReceiverFactory("err", func() component.ReceiverConfig { return &struct { @@ -435,6 +451,46 @@ func newErrExporterFactory() component.ExporterFactory { ) } +func newErrConnectorFactory() component.ConnectorFactory { + return component.NewConnectorFactory("err", func() component.ConnectorConfig { + return &struct { + config.ConnectorSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct + }{ + ConnectorSettings: config.NewConnectorSettings(component.NewID("bf")), + } + }, + component.WithTracesToTracesConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.TracesToTracesConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + component.WithTracesToMetricsConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.TracesToMetricsConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + component.WithTracesToLogsConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.TracesToLogsConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + + component.WithMetricsToTracesConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.MetricsToTracesConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + component.WithMetricsToMetricsConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.MetricsToMetricsConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + component.WithMetricsToLogsConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.MetricsToLogsConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + + component.WithLogsToTracesConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Traces) (component.LogsToTracesConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + component.WithLogsToMetricsConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Metrics) (component.LogsToMetricsConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + component.WithLogsToLogsConnector(func(context.Context, component.ConnectorCreateSettings, component.ConnectorConfig, consumer.Logs) (component.LogsToLogsConnector, error) { + return &errComponent{}, nil + }, component.StabilityLevelUnmaintained), + ) +} + func toSettings(factories component.Factories, cfg *configSettings) Settings { return Settings{ Telemetry: componenttest.NewNopTelemetrySettings(), @@ -445,6 +501,8 @@ func toSettings(factories component.Factories, cfg *configSettings) Settings { ProcessorConfigs: cfg.Processors.GetProcessors(), ExporterFactories: factories.Exporters, ExporterConfigs: cfg.Exporters.GetExporters(), + ConnectorFactories: factories.Connectors, + ConnectorConfigs: cfg.Connectors.GetConnectors(), PipelineConfigs: cfg.Service.Pipelines, } } @@ -465,11 +523,42 @@ func (e errComponent) Shutdown(context.Context) error { return errors.New("my error") } +func (e errComponent) ConsumeTracesToTraces(ctx context.Context, _ ptrace.Traces) error { + return e.ConsumeTraces(ctx, ptrace.NewTraces()) +} +func (e errComponent) ConsumeTracesToMetrics(ctx context.Context, _ ptrace.Traces) error { + return e.ConsumeMetrics(ctx, pmetric.NewMetrics()) +} +func (e errComponent) ConsumeTracesToLogs(ctx context.Context, _ ptrace.Traces) error { + return e.ConsumeLogs(ctx, plog.NewLogs()) +} + +func (e errComponent) ConsumeMetricsToTraces(ctx context.Context, _ pmetric.Metrics) error { + return e.ConsumeTraces(ctx, ptrace.NewTraces()) +} +func (e errComponent) ConsumeMetricsToMetrics(ctx context.Context, _ pmetric.Metrics) error { + return e.ConsumeMetrics(ctx, pmetric.NewMetrics()) +} +func (e errComponent) ConsumeMetricsToLogs(ctx context.Context, _ pmetric.Metrics) error { + return e.ConsumeLogs(ctx, plog.NewLogs()) +} + +func (e errComponent) ConsumeLogsToTraces(ctx context.Context, _ plog.Logs) error { + return e.ConsumeTraces(ctx, ptrace.NewTraces()) +} +func (e errComponent) ConsumeLogsToMetrics(ctx context.Context, _ plog.Logs) error { + return e.ConsumeMetrics(ctx, pmetric.NewMetrics()) +} +func (e errComponent) ConsumeLogsToLogs(ctx context.Context, _ plog.Logs) error { + return e.ConsumeLogs(ctx, plog.NewLogs()) +} + // TODO: Remove this by not reading the input from the files, or by providing something similar outside service package. type configSettings struct { Receivers *configunmarshaler.Receivers `mapstructure:"receivers"` Processors *configunmarshaler.Processors `mapstructure:"processors"` Exporters *configunmarshaler.Exporters `mapstructure:"exporters"` + Connectors *configunmarshaler.Connectors `mapstructure:"connectors"` Service *serviceSettings `mapstructure:"service"` } @@ -485,6 +574,7 @@ func loadConfig(t *testing.T, fileName string, factories component.Factories) *c Receivers: configunmarshaler.NewReceivers(factories.Receivers), Processors: configunmarshaler.NewProcessors(factories.Processors), Exporters: configunmarshaler.NewExporters(factories.Exporters), + Connectors: configunmarshaler.NewConnectors(factories.Connectors), } require.NoError(t, conf.Unmarshal(cfg, confmap.WithErrorUnused())) return cfg diff --git a/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_logs.yaml b/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_logs.yaml new file mode 100644 index 000000000000..530b1c849a83 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_logs.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + logs/in: + receivers: [nop] + exporters: [nop] + logs/out: + receivers: [nop, nop/conn] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_metrics.yaml b/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_metrics.yaml new file mode 100644 index 000000000000..54ab33cfcde3 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_metrics.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + metrics/in: + receivers: [nop] + exporters: [nop] + metrics/out: + receivers: [nop, nop/conn] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_traces.yaml b/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_traces.yaml new file mode 100644 index 000000000000..ce24befa42b4 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_conn_omit_exp_traces.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [nop] + traces/out: + receivers: [nop, nop/conn] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_logs.yaml b/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_logs.yaml new file mode 100644 index 000000000000..864c38f6282e --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_logs.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + logs/in: + receivers: [nop] + exporters: [nop, nop/conn] + logs/out: + receivers: [nop] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_metrics.yaml b/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_metrics.yaml new file mode 100644 index 000000000000..924779d1850a --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_metrics.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + metrics/in: + receivers: [nop] + exporters: [nop, nop/conn] + metrics/out: + receivers: [nop] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_traces.yaml b/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_traces.yaml new file mode 100644 index 000000000000..a3e34bb0253e --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_conn_omit_recv_traces.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [nop, nop/conn] + traces/out: + receivers: [nop] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_deep_cycle_logs.yaml b/service/internal/pipelines/testdata/not_allowed_deep_cycle_logs.yaml new file mode 100644 index 000000000000..2a0f033b8907 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_deep_cycle_logs.yaml @@ -0,0 +1,34 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/conn: + nop/conn1: + nop/conn2: + nop/conn3: + +service: + pipelines: + logs/in: + receivers: [nop] + processors: [nop] + exporters: [nop/conn] + logs/1: + receivers: [nop/conn] + processors: [nop] + exporters: [nop/conn1] + logs/2: + receivers: [nop/conn1] + processors: [nop] + exporters: [nop/conn2] + logs/3: + receivers: [nop/conn2] + processors: [nop] + exporters: [nop/conn3, nop/conn] # loops back + logs/out: + receivers: [nop/conn3] + processors: [nop] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_deep_cycle_metrics.yaml b/service/internal/pipelines/testdata/not_allowed_deep_cycle_metrics.yaml new file mode 100644 index 000000000000..523da5881297 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_deep_cycle_metrics.yaml @@ -0,0 +1,34 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/conn: + nop/conn1: + nop/conn2: + nop/conn3: + +service: + pipelines: + metrics/in: + receivers: [nop] + processors: [nop] + exporters: [nop/conn] + metrics/1: + receivers: [nop/conn] + processors: [nop] + exporters: [nop/conn1] + metrics/2: + receivers: [nop/conn1] + processors: [nop] + exporters: [nop/conn2] + metrics/3: + receivers: [nop/conn2] + processors: [nop] + exporters: [nop/conn3, nop/conn] # loops back + metrics/out: + receivers: [nop/conn3] + processors: [nop] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_deep_cycle_multi_signal.yaml b/service/internal/pipelines/testdata/not_allowed_deep_cycle_multi_signal.yaml new file mode 100644 index 000000000000..15ccc17f9a1e --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_deep_cycle_multi_signal.yaml @@ -0,0 +1,47 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/fork: + nop/count: + nop/forkagain: + nop/logit: + + +service: + pipelines: + traces/in: + receivers: [nop] + processors: [nop] + exporters: [nop/fork] + + traces/copy1: + receivers: [nop/fork] + processors: [nop] + exporters: [nop/count] + + traces/copy2: + receivers: [nop/fork] + processors: [nop] + exporters: [nop/forkagain] + + traces/copy2a: + receivers: [nop/forkagain] + processors: [nop] + exporters: [nop/count] + metrics/count: + receivers: [nop/count] + processors: [nop] + exporters: [nop] + + traces/copy2b: + receivers: [nop/forkagain] + processors: [nop] + exporters: [nop/rawlog] + logs/raw: + receivers: [nop/rawlog] + processors: [nop] + exporters: [nop, nop/fork] # cannot loop back to 'nop/fork' diff --git a/service/internal/pipelines/testdata/not_allowed_deep_cycle_traces.yaml b/service/internal/pipelines/testdata/not_allowed_deep_cycle_traces.yaml new file mode 100644 index 000000000000..a45473b2de43 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_deep_cycle_traces.yaml @@ -0,0 +1,34 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/conn: + nop/conn1: + nop/conn2: + nop/conn3: + +service: + pipelines: + traces/in: + receivers: [nop] + processors: [nop] + exporters: [nop/conn] + traces/1: + receivers: [nop/conn] + processors: [nop] + exporters: [nop/conn1] + traces/2: + receivers: [nop/conn1] + processors: [nop] + exporters: [nop/conn2] + traces/3: + receivers: [nop/conn2] + processors: [nop] + exporters: [nop/conn3, nop/conn] # loops back + traces/out: + receivers: [nop/conn3] + processors: [nop] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_allowed_simple_cycle_logs.yaml b/service/internal/pipelines/testdata/not_allowed_simple_cycle_logs.yaml new file mode 100644 index 000000000000..b6ac2fbe2c0c --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_simple_cycle_logs.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + logs: + receivers: [nop/conn] + processors: [nop] + exporters: [nop/conn] diff --git a/service/internal/pipelines/testdata/not_allowed_simple_cycle_metrics.yaml b/service/internal/pipelines/testdata/not_allowed_simple_cycle_metrics.yaml new file mode 100644 index 000000000000..6bcaa93d62db --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_simple_cycle_metrics.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + metrics: + receivers: [nop/conn] + processors: [nop] + exporters: [nop/conn] diff --git a/service/internal/pipelines/testdata/not_allowed_simple_cycle_traces.yaml b/service/internal/pipelines/testdata/not_allowed_simple_cycle_traces.yaml new file mode 100644 index 000000000000..106973028901 --- /dev/null +++ b/service/internal/pipelines/testdata/not_allowed_simple_cycle_traces.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +processors: + nop: +exporters: + nop: +connectors: + nop/conn: + +service: + pipelines: + traces: + receivers: [nop/conn] + processors: [nop] + exporters: [nop/conn] diff --git a/service/internal/pipelines/testdata/not_supported_connector_logs_logs.yaml b/service/internal/pipelines/testdata/not_supported_connector_logs_logs.yaml new file mode 100644 index 000000000000..e3fd4b8879d7 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_logs_logs.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + logs/in: + receivers: [nop] + exporters: [bf] + logs/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_logs_metrics.yaml b/service/internal/pipelines/testdata/not_supported_connector_logs_metrics.yaml new file mode 100644 index 000000000000..92e7a5ec5f1c --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_logs_metrics.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + logs/in: + receivers: [nop] + exporters: [bf] + metrics/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_logs_traces.yaml b/service/internal/pipelines/testdata/not_supported_connector_logs_traces.yaml new file mode 100644 index 000000000000..8545e4621863 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_logs_traces.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + logs/in: + receivers: [nop] + exporters: [bf] + traces/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_metrics_logs.yaml b/service/internal/pipelines/testdata/not_supported_connector_metrics_logs.yaml new file mode 100644 index 000000000000..697510402750 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_metrics_logs.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + metrics/in: + receivers: [nop] + exporters: [bf] + logs/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_metrics_metrics.yaml b/service/internal/pipelines/testdata/not_supported_connector_metrics_metrics.yaml new file mode 100644 index 000000000000..3becab1af7e8 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_metrics_metrics.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + metrics/in: + receivers: [nop] + exporters: [bf] + metrics/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_metrics_traces.yaml b/service/internal/pipelines/testdata/not_supported_connector_metrics_traces.yaml new file mode 100644 index 000000000000..993d2e58380f --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_metrics_traces.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + metrics/in: + receivers: [nop] + exporters: [bf] + traces/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_traces_logs.yaml b/service/internal/pipelines/testdata/not_supported_connector_traces_logs.yaml new file mode 100644 index 000000000000..659523669924 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_traces_logs.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [bf] + logs/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_traces_metrics.yaml b/service/internal/pipelines/testdata/not_supported_connector_traces_metrics.yaml new file mode 100644 index 000000000000..c009300aad28 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_traces_metrics.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [bf] + metrics/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/not_supported_connector_traces_traces.yaml b/service/internal/pipelines/testdata/not_supported_connector_traces_traces.yaml new file mode 100644 index 000000000000..ff5bbc176f18 --- /dev/null +++ b/service/internal/pipelines/testdata/not_supported_connector_traces_traces.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + bf: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [bf] + traces/out: + receivers: [bf] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/pipelines_conn_fork_merge_logs.yaml b/service/internal/pipelines/testdata/pipelines_conn_fork_merge_logs.yaml new file mode 100644 index 000000000000..357749610676 --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_fork_merge_logs.yaml @@ -0,0 +1,34 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor/prefork: + exampleprocessor/type0: + exampleprocessor/type1: + exampleprocessor/postmerge: + +exporters: + exampleexporter: + +connectors: + exampleconnector/fork: + exampleconnector/merge: + +service: + pipelines: + logs/in: + receivers: [examplereceiver] + processors: [exampleprocessor/prefork] + exporters: [exampleconnector/fork] + logs/type0: + receivers: [exampleconnector/fork] + processors: [exampleprocessor/type0] + exporters: [exampleconnector/merge] + logs/type1: + receivers: [exampleconnector/fork] + processors: [exampleprocessor/type1] + exporters: [exampleconnector/merge] + logs/out: + receivers: [exampleconnector/merge] + processors: [exampleprocessor/postmerge] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_fork_merge_metrics.yaml b/service/internal/pipelines/testdata/pipelines_conn_fork_merge_metrics.yaml new file mode 100644 index 000000000000..c51cc5906fd2 --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_fork_merge_metrics.yaml @@ -0,0 +1,34 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor/prefork: + exampleprocessor/type0: + exampleprocessor/type1: + exampleprocessor/postmerge: + +exporters: + exampleexporter: + +connectors: + exampleconnector/fork: + exampleconnector/merge: + +service: + pipelines: + metrics/in: + receivers: [examplereceiver] + processors: [exampleprocessor/prefork] + exporters: [exampleconnector/fork] + metrics/type0: + receivers: [exampleconnector/fork] + processors: [exampleprocessor/type0] + exporters: [exampleconnector/merge] + metrics/type1: + receivers: [exampleconnector/fork] + processors: [exampleprocessor/type1] + exporters: [exampleconnector/merge] + metrics/out: + receivers: [exampleconnector/merge] + processors: [exampleprocessor/postmerge] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_fork_merge_traces.yaml b/service/internal/pipelines/testdata/pipelines_conn_fork_merge_traces.yaml new file mode 100644 index 000000000000..5227f119f47f --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_fork_merge_traces.yaml @@ -0,0 +1,34 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor/prefork: + exampleprocessor/type0: + exampleprocessor/type1: + exampleprocessor/postmerge: + +exporters: + exampleexporter: + +connectors: + exampleconnector/fork: + exampleconnector/merge: + +service: + pipelines: + traces/in: + receivers: [examplereceiver] + processors: [exampleprocessor/prefork] + exporters: [exampleconnector/fork] + traces/type0: + receivers: [exampleconnector/fork] + processors: [exampleprocessor/type0] + exporters: [exampleconnector/merge] + traces/type1: + receivers: [exampleconnector/fork] + processors: [exampleprocessor/type1] + exporters: [exampleconnector/merge] + traces/out: + receivers: [exampleconnector/merge] + processors: [exampleprocessor/postmerge] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_matrix.yaml b/service/internal/pipelines/testdata/pipelines_conn_matrix.yaml new file mode 100644 index 000000000000..9b813538870f --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_matrix.yaml @@ -0,0 +1,39 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + traces/in: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + metrics/in: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + logs/in: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + + traces/out: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] + metrics/out: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] + logs/out: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_simple_logs.yaml b/service/internal/pipelines/testdata/pipelines_conn_simple_logs.yaml new file mode 100644 index 000000000000..60887f4b1a79 --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_simple_logs.yaml @@ -0,0 +1,22 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + logs/in: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + logs/out: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_simple_metrics.yaml b/service/internal/pipelines/testdata/pipelines_conn_simple_metrics.yaml new file mode 100644 index 000000000000..a8426533ee83 --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_simple_metrics.yaml @@ -0,0 +1,22 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + metrics/in: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + metrics/out: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_simple_traces.yaml b/service/internal/pipelines/testdata/pipelines_conn_simple_traces.yaml new file mode 100644 index 000000000000..b0fc29e209ca --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_simple_traces.yaml @@ -0,0 +1,22 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + traces/in: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + traces/out: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_translate_from_logs.yaml b/service/internal/pipelines/testdata/pipelines_conn_translate_from_logs.yaml new file mode 100644 index 000000000000..91cac17cbd2a --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_translate_from_logs.yaml @@ -0,0 +1,26 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + logs: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + traces: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] + metrics: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_translate_from_metrics.yaml b/service/internal/pipelines/testdata/pipelines_conn_translate_from_metrics.yaml new file mode 100644 index 000000000000..bf286afcedb0 --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_translate_from_metrics.yaml @@ -0,0 +1,26 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + metrics: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + traces: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] + logs: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/pipelines_conn_translate_from_traces.yaml b/service/internal/pipelines/testdata/pipelines_conn_translate_from_traces.yaml new file mode 100644 index 000000000000..61d4dea5808e --- /dev/null +++ b/service/internal/pipelines/testdata/pipelines_conn_translate_from_traces.yaml @@ -0,0 +1,26 @@ +receivers: + examplereceiver: + +processors: + exampleprocessor: + +exporters: + exampleexporter: + +connectors: + exampleconnector: + +service: + pipelines: + traces: + receivers: [examplereceiver] + processors: [exampleprocessor] + exporters: [exampleconnector] + metrics: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] + logs: + receivers: [exampleconnector] + processors: [exampleprocessor] + exporters: [exampleexporter] diff --git a/service/internal/pipelines/testdata/unknown_connector_config.yaml b/service/internal/pipelines/testdata/unknown_connector_config.yaml new file mode 100644 index 000000000000..c10c40d2a4a2 --- /dev/null +++ b/service/internal/pipelines/testdata/unknown_connector_config.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + nop: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [nop/1] + traces/out: + receivers: [nop/1] + exporters: [nop] diff --git a/service/internal/pipelines/testdata/unknown_connector_factory.yaml b/service/internal/pipelines/testdata/unknown_connector_factory.yaml new file mode 100644 index 000000000000..6778c0e2141f --- /dev/null +++ b/service/internal/pipelines/testdata/unknown_connector_factory.yaml @@ -0,0 +1,15 @@ +receivers: + nop: +exporters: + nop: +connectors: + unknown: + +service: + pipelines: + traces/in: + receivers: [nop] + exporters: [unknown] + traces/out: + receivers: [unknown] + exporters: [nop] diff --git a/service/internal/testcomponents/example_connector.go b/service/internal/testcomponents/example_connector.go new file mode 100644 index 000000000000..a1f05b67cddc --- /dev/null +++ b/service/internal/testcomponents/example_connector.go @@ -0,0 +1,158 @@ +// 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 testcomponents // import "go.opentelemetry.io/collector/service/internal/testcomponents" + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/internal/testdata" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +const connType = "exampleconnector" + +// ExampleConnectorConfig config for ExampleConnector. +type ExampleConnectorConfig struct { + config.ConnectorSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct +} + +// ExampleConnectorFactory is factory for ExampleConnector. +var ExampleConnectorFactory = component.NewConnectorFactory( + connType, + createExampleConnectorDefaultConfig, + + component.WithTracesToTracesConnector(createExampleTracesToTracesConnector, component.StabilityLevelDevelopment), + component.WithTracesToMetricsConnector(createTranslateTracesToMetricsConnector, component.StabilityLevelDevelopment), + component.WithTracesToLogsConnector(createTranslateTracesToLogsConnector, component.StabilityLevelDevelopment), + + component.WithMetricsToTracesConnector(createTranslateMetricsToTracesConnector, component.StabilityLevelDevelopment), + component.WithMetricsToMetricsConnector(createExampleMetricsToMetricsConnector, component.StabilityLevelDevelopment), + component.WithMetricsToLogsConnector(createTranslateMetricsToLogsConnector, component.StabilityLevelDevelopment), + + component.WithLogsToTracesConnector(createTranslateLogsToTracesConnector, component.StabilityLevelDevelopment), + component.WithLogsToMetricsConnector(createTranslateLogsToMetricsConnector, component.StabilityLevelDevelopment), + component.WithLogsToLogsConnector(createExampleLogsToLogsConnector, component.StabilityLevelDevelopment), +) + +func createExampleConnectorDefaultConfig() component.ConnectorConfig { + return &ExampleConnectorConfig{ + ConnectorSettings: config.NewConnectorSettings(component.NewID(typeStr)), + } +} + +func createExampleTracesToTracesConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, traces consumer.Traces) (component.TracesToTracesConnector, error) { + return &ExampleConnector{Traces: traces}, nil +} +func createTranslateTracesToMetricsConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, metrics consumer.Metrics) (component.TracesToMetricsConnector, error) { + return &ExampleConnector{Metrics: metrics}, nil +} +func createTranslateTracesToLogsConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, logs consumer.Logs) (component.TracesToLogsConnector, error) { + return &ExampleConnector{Logs: logs}, nil +} + +func createTranslateMetricsToTracesConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, traces consumer.Traces) (component.MetricsToTracesConnector, error) { + return &ExampleConnector{Traces: traces}, nil +} +func createExampleMetricsToMetricsConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, metrics consumer.Metrics) (component.MetricsToMetricsConnector, error) { + return &ExampleConnector{Metrics: metrics}, nil +} +func createTranslateMetricsToLogsConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, logs consumer.Logs) (component.MetricsToLogsConnector, error) { + return &ExampleConnector{Logs: logs}, nil +} + +func createTranslateLogsToTracesConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, traces consumer.Traces) (component.LogsToTracesConnector, error) { + return &ExampleConnector{Traces: traces}, nil +} +func createTranslateLogsToMetricsConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, metrics consumer.Metrics) (component.LogsToMetricsConnector, error) { + return &ExampleConnector{Metrics: metrics}, nil +} +func createExampleLogsToLogsConnector(_ context.Context, _ component.ConnectorCreateSettings, _ component.ConnectorConfig, logs consumer.Logs) (component.LogsToLogsConnector, error) { + return &ExampleConnector{Logs: logs}, nil +} + +var _ StatefulComponent = &ExampleConnector{} + +type ExampleConnector struct { + Traces consumer.Traces + Metrics consumer.Metrics + Logs consumer.Logs + componentState +} + +// Start tells the Connector to start. +func (c *ExampleConnector) Start(_ context.Context, _ component.Host) error { + c.started = true + return nil +} + +// ConsumeTraces receives ptrace.Traces for processing by the consumer.Traces. +func (c *ExampleConnector) ConsumeTracesToTraces(ctx context.Context, td ptrace.Traces) error { + return c.Traces.ConsumeTraces(ctx, td) +} + +// ConsumeTracesToMetrics receives ptrace.Traces and emits pmetric.Metrics to consumer.Metrics. +func (c *ExampleConnector) ConsumeTracesToMetrics(ctx context.Context, td ptrace.Traces) error { + return c.Metrics.ConsumeMetrics(ctx, testdata.GenerateMetrics(td.SpanCount())) +} + +// ConsumeTracesToLogs receives ptrace.Traces and emits pmetric.Logs to consumer.Logs. +func (c *ExampleConnector) ConsumeTracesToLogs(ctx context.Context, td ptrace.Traces) error { + return c.Logs.ConsumeLogs(ctx, testdata.GenerateLogs(td.SpanCount())) +} + +// ConsumeMetricsToTraces receives pmetric.Metrics and emits pmetric.Traces to consumer.Traces. +func (c *ExampleConnector) ConsumeMetricsToTraces(ctx context.Context, md pmetric.Metrics) error { + return c.Traces.ConsumeTraces(ctx, testdata.GenerateTraces(md.MetricCount())) +} + +// ConsumeMetrics receives pmetric.Metrics for processing by the Metrics. +func (c *ExampleConnector) ConsumeMetricsToMetrics(ctx context.Context, md pmetric.Metrics) error { + return c.Metrics.ConsumeMetrics(ctx, md) +} + +// ConsumeMetricsToLogs receives pmetric.Metrics and emits pmetric.Logs to consumer.Logs. +func (c *ExampleConnector) ConsumeMetricsToLogs(ctx context.Context, md pmetric.Metrics) error { + return c.Logs.ConsumeLogs(ctx, testdata.GenerateLogs(md.MetricCount())) +} + +// ConsumeLogsToTraces receives plog.Logs and emits pmetric.Traces to consumer.Traces. +func (c *ExampleConnector) ConsumeLogsToTraces(ctx context.Context, ld plog.Logs) error { + return c.Traces.ConsumeTraces(ctx, testdata.GenerateTraces(ld.LogRecordCount())) +} + +// ConsumeLogsToMetrics receives plog.Logs and emits pmetric.Metrics to consumer.Metrics. +func (c *ExampleConnector) ConsumeLogsToMetrics(ctx context.Context, ld plog.Logs) error { + return c.Metrics.ConsumeMetrics(ctx, testdata.GenerateMetrics(ld.LogRecordCount())) +} + +// ConsumeLogs receives pmetric.Logs for processing by the Metrics. +func (c *ExampleConnector) ConsumeLogsToLogs(ctx context.Context, ld plog.Logs) error { + return c.Logs.ConsumeLogs(ctx, ld) +} + +// Shutdown is invoked during shutdown. +func (c *ExampleConnector) Shutdown(context.Context) error { + c.stopped = true + return nil +} + +func (c *ExampleConnector) Capabilities() consumer.Capabilities { + return consumer.Capabilities{MutatesData: true} +} diff --git a/service/internal/testcomponents/example_connector_test.go b/service/internal/testcomponents/example_connector_test.go new file mode 100644 index 000000000000..2877dc00b868 --- /dev/null +++ b/service/internal/testcomponents/example_connector_test.go @@ -0,0 +1,36 @@ +// 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 testcomponents + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/collector/component/componenttest" +) + +func TestExampleConnector(t *testing.T) { + conn := &ExampleConnector{} + host := componenttest.NewNopHost() + assert.False(t, conn.Started()) + assert.NoError(t, conn.Start(context.Background(), host)) + assert.True(t, conn.Started()) + + assert.False(t, conn.Stopped()) + assert.NoError(t, conn.Shutdown(context.Background())) + assert.True(t, conn.Stopped()) +} diff --git a/service/internal/testcomponents/example_exporter.go b/service/internal/testcomponents/example_exporter.go index bc680b79acf5..9d5b29cf355d 100644 --- a/service/internal/testcomponents/example_exporter.go +++ b/service/internal/testcomponents/example_exporter.go @@ -62,26 +62,24 @@ func createLogsExporter(context.Context, component.ExporterCreateSettings, compo return &ExampleExporter{}, nil } +var _ StatefulComponent = &ExampleExporter{} + // ExampleExporter stores consumed traces and metrics for testing purposes. type ExampleExporter struct { - Traces []ptrace.Traces - Metrics []pmetric.Metrics - Logs []plog.Logs - Started bool - Stopped bool + componentState } // Start tells the exporter to start. The exporter may prepare for exporting // by connecting to the endpoint. Host parameter can be used for communicating // with the host after Start() has already returned. func (exp *ExampleExporter) Start(_ context.Context, _ component.Host) error { - exp.Started = true + exp.started = true return nil } // ConsumeTraces receives ptrace.Traces for processing by the consumer.Traces. func (exp *ExampleExporter) ConsumeTraces(_ context.Context, td ptrace.Traces) error { - exp.Traces = append(exp.Traces, td) + exp.traces = append(exp.traces, td) return nil } @@ -91,17 +89,17 @@ func (exp *ExampleExporter) Capabilities() consumer.Capabilities { // ConsumeMetrics receives pmetric.Metrics for processing by the Metrics. func (exp *ExampleExporter) ConsumeMetrics(_ context.Context, md pmetric.Metrics) error { - exp.Metrics = append(exp.Metrics, md) + exp.metrics = append(exp.metrics, md) return nil } func (exp *ExampleExporter) ConsumeLogs(_ context.Context, ld plog.Logs) error { - exp.Logs = append(exp.Logs, ld) + exp.logs = append(exp.logs, ld) return nil } // Shutdown is invoked during shutdown. func (exp *ExampleExporter) Shutdown(context.Context) error { - exp.Stopped = true + exp.stopped = true return nil } diff --git a/service/internal/testcomponents/example_exporter_test.go b/service/internal/testcomponents/example_exporter_test.go index ddc94f6d7a5b..2ecdfb5dd5ec 100644 --- a/service/internal/testcomponents/example_exporter_test.go +++ b/service/internal/testcomponents/example_exporter_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" ) @@ -28,19 +29,23 @@ import ( func TestExampleExporter(t *testing.T) { exp := &ExampleExporter{} host := componenttest.NewNopHost() - assert.False(t, exp.Started) + assert.False(t, exp.Started()) assert.NoError(t, exp.Start(context.Background(), host)) - assert.True(t, exp.Started) + assert.True(t, exp.Started()) - assert.Equal(t, 0, len(exp.Traces)) + assert.Equal(t, 0, len(exp.traces)) assert.NoError(t, exp.ConsumeTraces(context.Background(), ptrace.Traces{})) - assert.Equal(t, 1, len(exp.Traces)) + assert.Equal(t, 1, len(exp.traces)) - assert.Equal(t, 0, len(exp.Metrics)) + assert.Equal(t, 0, len(exp.metrics)) assert.NoError(t, exp.ConsumeMetrics(context.Background(), pmetric.Metrics{})) - assert.Equal(t, 1, len(exp.Metrics)) + assert.Equal(t, 1, len(exp.metrics)) - assert.False(t, exp.Stopped) + assert.Equal(t, 0, len(exp.logs)) + assert.NoError(t, exp.ConsumeLogs(context.Background(), plog.Logs{})) + assert.Equal(t, 1, len(exp.logs)) + + assert.False(t, exp.Stopped()) assert.NoError(t, exp.Shutdown(context.Background())) - assert.True(t, exp.Stopped) + assert.True(t, exp.Stopped()) } diff --git a/service/internal/testcomponents/example_factories.go b/service/internal/testcomponents/example_factories.go index ac5f21f433fe..6d43c81ba1ce 100644 --- a/service/internal/testcomponents/example_factories.go +++ b/service/internal/testcomponents/example_factories.go @@ -16,6 +16,9 @@ package testcomponents // import "go.opentelemetry.io/collector/service/internal import ( "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" ) // ExampleComponents registers example factories. This is only used by tests. @@ -30,5 +33,45 @@ func ExampleComponents() (component.Factories, error) { Exporters: map[component.Type]component.ExporterFactory{ ExampleExporterFactory.Type(): ExampleExporterFactory, }, + Connectors: map[component.Type]component.ConnectorFactory{ + ExampleConnectorFactory.Type(): ExampleConnectorFactory, + }, }, nil } + +type StatefulComponent interface { + component.Component + Started() bool + Stopped() bool + RecallTraces() []ptrace.Traces + RecallMetrics() []pmetric.Metrics + RecallLogs() []plog.Logs +} + +type componentState struct { + started bool + stopped bool + traces []ptrace.Traces + metrics []pmetric.Metrics + logs []plog.Logs +} + +func (cs *componentState) Started() bool { + return cs.started +} + +func (cs *componentState) Stopped() bool { + return cs.stopped +} + +func (cs *componentState) RecallTraces() []ptrace.Traces { + return cs.traces +} + +func (cs *componentState) RecallMetrics() []pmetric.Metrics { + return cs.metrics +} + +func (cs *componentState) RecallLogs() []plog.Logs { + return cs.logs +} diff --git a/service/internal/testcomponents/example_processor.go b/service/internal/testcomponents/example_processor.go index e7e573c8cf81..1d7ce9adff96 100644 --- a/service/internal/testcomponents/example_processor.go +++ b/service/internal/testcomponents/example_processor.go @@ -56,21 +56,22 @@ func createLogsProcessor(_ context.Context, _ component.ProcessorCreateSettings, return &ExampleProcessor{Logs: nextConsumer}, nil } +var _ StatefulComponent = &ExampleProcessor{} + type ExampleProcessor struct { consumer.Traces consumer.Metrics consumer.Logs - Started bool - Stopped bool + componentState } func (ep *ExampleProcessor) Start(_ context.Context, _ component.Host) error { - ep.Started = true + ep.started = true return nil } func (ep *ExampleProcessor) Shutdown(_ context.Context) error { - ep.Stopped = true + ep.stopped = true return nil } diff --git a/service/internal/testcomponents/example_processor_test.go b/service/internal/testcomponents/example_processor_test.go index 763f5b24825f..25385fba0311 100644 --- a/service/internal/testcomponents/example_processor_test.go +++ b/service/internal/testcomponents/example_processor_test.go @@ -26,11 +26,11 @@ import ( func TestExampleProcessor(t *testing.T) { prc := &ExampleProcessor{} host := componenttest.NewNopHost() - assert.False(t, prc.Started) + assert.False(t, prc.Started()) assert.NoError(t, prc.Start(context.Background(), host)) - assert.True(t, prc.Started) + assert.True(t, prc.Started()) - assert.False(t, prc.Stopped) + assert.False(t, prc.Stopped()) assert.NoError(t, prc.Shutdown(context.Background())) - assert.True(t, prc.Stopped) + assert.True(t, prc.Stopped()) } diff --git a/service/internal/testcomponents/example_receiver.go b/service/internal/testcomponents/example_receiver.go index dfe0a1ce2f9f..fb9fc9b3dfd3 100644 --- a/service/internal/testcomponents/example_receiver.go +++ b/service/internal/testcomponents/example_receiver.go @@ -93,24 +93,25 @@ func createReceiver(cfg component.ReceiverConfig) *ExampleReceiver { return receiver } +var _ StatefulComponent = &ExampleReceiver{} + // ExampleReceiver allows producing traces and metrics for testing purposes. type ExampleReceiver struct { consumer.Traces consumer.Metrics consumer.Logs - Started bool - Stopped bool + componentState } // Start tells the receiver to start its processing. func (erp *ExampleReceiver) Start(_ context.Context, _ component.Host) error { - erp.Started = true + erp.started = true return nil } // Shutdown tells the receiver that should stop reception, func (erp *ExampleReceiver) Shutdown(context.Context) error { - erp.Stopped = true + erp.stopped = true return nil } diff --git a/service/internal/testcomponents/example_receiver_test.go b/service/internal/testcomponents/example_receiver_test.go index 32f415cc0585..53ce6e93dc08 100644 --- a/service/internal/testcomponents/example_receiver_test.go +++ b/service/internal/testcomponents/example_receiver_test.go @@ -26,11 +26,11 @@ import ( func TestExampleReceiver(t *testing.T) { rcv := &ExampleReceiver{} host := componenttest.NewNopHost() - assert.False(t, rcv.Started) + assert.False(t, rcv.Started()) assert.NoError(t, rcv.Start(context.Background(), host)) - assert.True(t, rcv.Started) + assert.True(t, rcv.Started()) - assert.False(t, rcv.Stopped) + assert.False(t, rcv.Stopped()) assert.NoError(t, rcv.Shutdown(context.Background())) - assert.True(t, rcv.Stopped) + assert.True(t, rcv.Stopped()) } diff --git a/service/service.go b/service/service.go index 261ec7377e8c..e89c30a1f698 100644 --- a/service/service.go +++ b/service/service.go @@ -25,12 +25,28 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/service/extensions" "go.opentelemetry.io/collector/service/internal/pipelines" "go.opentelemetry.io/collector/service/internal/proctelemetry" "go.opentelemetry.io/collector/service/telemetry" ) +const ( + connectorsFeatureGateID = "service.enableConnectors" + connectorsFeatureStage = featuregate.StageAlpha +) + +func init() { + featuregate.GetRegistry().MustRegisterID( + connectorsFeatureGateID, + connectorsFeatureStage, + featuregate.WithRegisterDescription("Enables 'conectors', a new type of component for transmitting signals between pipelines. "+ + "This change includes a major rewrite of the collector's internal pipeline and component management logic."), + featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector/issues/2336"), + ) +} + // service represents the implementation of a component.Host. type service struct { buildInfo component.BuildInfo @@ -157,10 +173,19 @@ func (srv *service) initExtensionsAndPipeline(set *settings) error { ProcessorConfigs: srv.config.Processors, ExporterFactories: srv.host.factories.Exporters, ExporterConfigs: srv.config.Exporters, + ConnectorFactories: srv.host.factories.Connectors, + ConnectorConfigs: srv.config.Connectors, PipelineConfigs: srv.config.Service.Pipelines, } - if srv.host.pipelines, err = pipelines.Build(context.Background(), pipelinesSettings); err != nil { - return fmt.Errorf("cannot build pipelines: %w", err) + + if featuregate.GetRegistry().IsEnabled(connectorsFeatureGateID) { + if srv.host.pipelines, err = pipelines.NewPipelinesGraph(context.Background(), pipelinesSettings); err != nil { + return fmt.Errorf("cannot build pipelines: %w", err) + } + } else { + if srv.host.pipelines, err = pipelines.Build(context.Background(), pipelinesSettings); err != nil { + return fmt.Errorf("cannot build pipelines: %w", err) + } } if set.Config.Service.Telemetry.Metrics.Level != configtelemetry.LevelNone && set.Config.Service.Telemetry.Metrics.Address != "" { diff --git a/service/servicetest/configprovider_test.go b/service/servicetest/configprovider_test.go index e022eb76c141..39cd7e7b22f7 100644 --- a/service/servicetest/configprovider_test.go +++ b/service/servicetest/configprovider_test.go @@ -53,6 +53,10 @@ func TestLoadConfig(t *testing.T) { assert.Contains(t, cfg.Processors, component.NewID("nop")) assert.Contains(t, cfg.Processors, component.NewIDWithName("nop", "myprocessor")) + // Verify connectors + assert.Len(t, cfg.Connectors, 1) + assert.Contains(t, cfg.Connectors, component.NewIDWithName("nop", "myconnector")) + // Verify service. require.Len(t, cfg.Service.Extensions, 1) assert.Contains(t, cfg.Service.Extensions, component.NewID("nop")) diff --git a/service/servicetest/testdata/config.yaml b/service/servicetest/testdata/config.yaml index 38227d7a68bc..d9e21c55369c 100644 --- a/service/servicetest/testdata/config.yaml +++ b/service/servicetest/testdata/config.yaml @@ -10,6 +10,9 @@ exporters: nop: nop/myexporter: +connectors: + nop/myconnector: + extensions: nop: nop/myextension: diff --git a/service/unmarshaler.go b/service/unmarshaler.go index c1c78d19a048..ab5baf4e7b89 100644 --- a/service/unmarshaler.go +++ b/service/unmarshaler.go @@ -29,6 +29,7 @@ type configSettings struct { Processors *configunmarshaler.Processors `mapstructure:"processors"` Exporters *configunmarshaler.Exporters `mapstructure:"exporters"` Extensions *configunmarshaler.Extensions `mapstructure:"extensions"` + Connectors *configunmarshaler.Connectors `mapstructure:"connectors"` Service ConfigService `mapstructure:"service"` } @@ -41,6 +42,7 @@ func unmarshal(v *confmap.Conf, factories component.Factories) (*configSettings, Processors: configunmarshaler.NewProcessors(factories.Processors), Exporters: configunmarshaler.NewExporters(factories.Exporters), Extensions: configunmarshaler.NewExtensions(factories.Extensions), + Connectors: configunmarshaler.NewConnectors(factories.Connectors), // TODO: Add a component.ServiceFactory to allow this to be defined by the Service. Service: ConfigService{ Telemetry: telemetry.Config{ diff --git a/versions.yaml b/versions.yaml index 2f992ad51bf4..50794b1d8015 100644 --- a/versions.yaml +++ b/versions.yaml @@ -24,6 +24,8 @@ module-sets: - go.opentelemetry.io/collector/component - go.opentelemetry.io/collector/consumer - go.opentelemetry.io/collector/cmd/builder + - go.opentelemetry.io/collector/connector/countconnector + - go.opentelemetry.io/collector/connector/nopconnector - go.opentelemetry.io/collector/exporter/loggingexporter - go.opentelemetry.io/collector/exporter/otlpexporter - go.opentelemetry.io/collector/exporter/otlphttpexporter