opentelemetry-collector/service/internal/builder/receivers_builder_test.go

354 lines
11 KiB
Go

// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package builder
import (
"context"
"path"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/configtest"
"go.opentelemetry.io/collector/internal/testcomponents"
"go.opentelemetry.io/collector/internal/testdata"
"go.opentelemetry.io/collector/model/pdata"
"go.opentelemetry.io/collector/processor/attributesprocessor"
)
type testCase struct {
name string
receiverID config.ComponentID
exporterIDs []config.ComponentID
spanDuplicationByExporter map[config.ComponentID]int
hasTraces bool
hasMetrics bool
}
func TestBuildReceivers(t *testing.T) {
tests := []testCase{
{
name: "one-exporter",
receiverID: config.NewID("examplereceiver"),
exporterIDs: []config.ComponentID{config.NewID("exampleexporter")},
hasTraces: true,
hasMetrics: true,
},
{
name: "multi-exporter",
receiverID: config.NewIDWithName("examplereceiver", "2"),
exporterIDs: []config.ComponentID{config.NewID("exampleexporter"), config.NewIDWithName("exampleexporter", "2")},
hasTraces: true,
},
{
name: "multi-metrics-receiver",
receiverID: config.NewIDWithName("examplereceiver", "3"),
exporterIDs: []config.ComponentID{config.NewID("exampleexporter"), config.NewIDWithName("exampleexporter", "2")},
hasTraces: false,
hasMetrics: true,
},
{
name: "multi-receiver-multi-exporter",
receiverID: config.NewIDWithName("examplereceiver", "multi"),
exporterIDs: []config.ComponentID{config.NewID("exampleexporter"), config.NewIDWithName("exampleexporter", "2")},
// Check pipelines_builder.yaml to understand this case.
// We have 2 pipelines, one exporting to one exporter, the other
// exporting to both exporters, so we expect a duplication on
// one of the exporters, but not on the other.
spanDuplicationByExporter: map[config.ComponentID]int{
config.NewID("exampleexporter"): 2, config.NewIDWithName("exampleexporter", "2"): 1,
},
hasTraces: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
testReceivers(t, test)
})
}
}
func testReceivers(
t *testing.T,
test testCase,
) {
factories, err := testcomponents.ExampleComponents()
assert.NoError(t, err)
attrFactory := attributesprocessor.NewFactory()
factories.Processors[attrFactory.Type()] = attrFactory
cfg, err := configtest.LoadConfigAndValidate("testdata/pipelines_builder.yaml", factories)
require.NoError(t, err)
// Build the pipeline
allExporters, err := BuildExporters(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, factories.Exporters)
assert.NoError(t, err)
pipelineProcessors, err := BuildPipelines(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, allExporters, factories.Processors)
assert.NoError(t, err)
receivers, err := BuildReceivers(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, pipelineProcessors, factories.Receivers)
assert.NoError(t, err)
require.NotNil(t, receivers)
receiver := receivers[cfg.Receivers[test.receiverID]]
// Ensure receiver has its fields correctly populated.
require.NotNil(t, receiver)
assert.NotNil(t, receiver.receiver)
// Compose the list of created exporters.
var exporters []*builtExporter
for _, name := range test.exporterIDs {
// Ensure exporter is created.
exp := allExporters[cfg.Exporters[name]]
require.NotNil(t, exp)
exporters = append(exporters, exp)
}
// Send TraceData via receiver and verify that all exporters of the pipeline receive it.
// First check that there are no traces in the exporters yet.
for _, exporter := range exporters {
consumer := exporter.getTracesExporter().(*testcomponents.ExampleExporterConsumer)
require.Equal(t, len(consumer.Traces), 0)
require.Equal(t, len(consumer.Metrics), 0)
}
td := testdata.GenerateTracesOneSpan()
if test.hasTraces {
traceProducer := receiver.receiver.(*testcomponents.ExampleReceiverProducer)
assert.NoError(t, traceProducer.ConsumeTraces(context.Background(), td))
}
md := testdata.GenerateMetricsOneMetric()
if test.hasMetrics {
metricsProducer := receiver.receiver.(*testcomponents.ExampleReceiverProducer)
assert.NoError(t, metricsProducer.ConsumeMetrics(context.Background(), md))
}
// Now verify received data.
for _, name := range test.exporterIDs {
// Check that the data is received by exporter.
exporter := allExporters[cfg.Exporters[name]]
// Validate traces.
if test.hasTraces {
var spanDuplicationCount int
if test.spanDuplicationByExporter != nil {
spanDuplicationCount = test.spanDuplicationByExporter[name]
} else {
spanDuplicationCount = 1
}
traceConsumer := exporter.getTracesExporter().(*testcomponents.ExampleExporterConsumer)
require.Equal(t, spanDuplicationCount, len(traceConsumer.Traces))
for i := 0; i < spanDuplicationCount; i++ {
assert.EqualValues(t, td, traceConsumer.Traces[i])
}
}
// Validate metrics.
if test.hasMetrics {
metricsConsumer := exporter.getMetricExporter().(*testcomponents.ExampleExporterConsumer)
require.Equal(t, 1, len(metricsConsumer.Metrics))
assert.EqualValues(t, md, metricsConsumer.Metrics[0])
}
}
}
func TestBuildReceivers_BuildCustom(t *testing.T) {
factories := createTestFactories()
tests := []struct {
dataType string
shouldFail bool
}{
{
dataType: "logs",
shouldFail: false,
},
{
dataType: "nosuchdatatype",
shouldFail: true,
},
}
for _, test := range tests {
t.Run(test.dataType, func(t *testing.T) {
dataType := test.dataType
cfg := createExampleConfig(dataType)
// Build the pipeline
allExporters, err := BuildExporters(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, factories.Exporters)
if test.shouldFail {
assert.Error(t, err)
return
}
assert.NoError(t, err)
pipelineProcessors, err := BuildPipelines(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, allExporters, factories.Processors)
assert.NoError(t, err)
receivers, err := BuildReceivers(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, pipelineProcessors, factories.Receivers)
assert.NoError(t, err)
require.NotNil(t, receivers)
receiver := receivers[cfg.Receivers[config.NewID("examplereceiver")]]
// Ensure receiver has its fields correctly populated.
require.NotNil(t, receiver)
assert.NotNil(t, receiver.receiver)
// Compose the list of created exporters.
exporterNames := []config.ComponentID{config.NewID("exampleexporter")}
var exporters []*builtExporter
for _, name := range exporterNames {
// Ensure exporter is created.
exp := allExporters[cfg.Exporters[name]]
require.NotNil(t, exp)
exporters = append(exporters, exp)
}
// Send Data via receiver and verify that all exporters of the pipeline receive it.
// First check that there are no traces in the exporters yet.
for _, exporter := range exporters {
consumer := exporter.getLogExporter().(*testcomponents.ExampleExporterConsumer)
require.Equal(t, len(consumer.Logs), 0)
}
// Send one data.
log := pdata.Logs{}
producer := receiver.receiver.(*testcomponents.ExampleReceiverProducer)
require.NoError(t, producer.ConsumeLogs(context.Background(), log))
// Now verify received data.
for _, name := range exporterNames {
// Check that the data is received by exporter.
exporter := allExporters[cfg.Exporters[name]]
// Validate exported data.
consumer := exporter.getLogExporter().(*testcomponents.ExampleExporterConsumer)
require.Equal(t, 1, len(consumer.Logs))
assert.EqualValues(t, log, consumer.Logs[0])
}
})
}
}
func TestBuildReceivers_StartAll(t *testing.T) {
receivers := make(Receivers)
rcvCfg := &testcomponents.ExampleReceiver{}
receiver := &testcomponents.ExampleReceiverProducer{}
receivers[rcvCfg] = &builtReceiver{
logger: zap.NewNop(),
receiver: receiver,
}
assert.False(t, receiver.Started)
err := receivers.StartAll(context.Background(), componenttest.NewNopHost())
assert.NoError(t, err)
assert.True(t, receiver.Started)
}
func TestBuildReceivers_StopAll(t *testing.T) {
receivers := make(Receivers)
rcvCfg := &testcomponents.ExampleReceiver{}
receiver := &testcomponents.ExampleReceiverProducer{}
receivers[rcvCfg] = &builtReceiver{
logger: zap.NewNop(),
receiver: receiver,
}
assert.False(t, receiver.Stopped)
assert.NoError(t, receivers.ShutdownAll(context.Background()))
assert.True(t, receiver.Stopped)
}
func TestBuildReceivers_Unused(t *testing.T) {
factories, err := testcomponents.ExampleComponents()
assert.NoError(t, err)
cfg, err := configtest.LoadConfigAndValidate("testdata/unused_receiver.yaml", factories)
assert.NoError(t, err)
// Build the pipeline
allExporters, err := BuildExporters(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, factories.Exporters)
assert.NoError(t, err)
pipelineProcessors, err := BuildPipelines(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, allExporters, factories.Processors)
assert.NoError(t, err)
receivers, err := BuildReceivers(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, pipelineProcessors, factories.Receivers)
assert.NoError(t, err)
assert.NotNil(t, receivers)
assert.NoError(t, receivers.StartAll(context.Background(), componenttest.NewNopHost()))
assert.NoError(t, receivers.ShutdownAll(context.Background()))
}
func TestBuildReceivers_NotSupportedDataType(t *testing.T) {
factories := createTestFactories()
tests := []struct {
configFile string
}{
{
configFile: "not_supported_receiver_logs.yaml",
},
{
configFile: "not_supported_receiver_metrics.yaml",
},
{
configFile: "not_supported_receiver_traces.yaml",
},
}
for _, test := range tests {
t.Run(test.configFile, func(t *testing.T) {
cfg, err := configtest.LoadConfigAndValidate(path.Join("testdata", test.configFile), factories)
assert.NoError(t, err)
require.NotNil(t, cfg)
allExporters, err := BuildExporters(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, factories.Exporters)
assert.NoError(t, err)
pipelineProcessors, err := BuildPipelines(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, allExporters, factories.Processors)
assert.NoError(t, err)
receivers, err := BuildReceivers(zap.NewNop(), trace.NewNoopTracerProvider(), component.DefaultBuildInfo(), cfg, pipelineProcessors, factories.Receivers)
assert.Error(t, err)
assert.Zero(t, len(receivers))
})
}
}