Skip to content

Commit

Permalink
Add lifecycle tests to all default components (#2741)
Browse files Browse the repository at this point in the history
* Add lifecycle tests to all defaultcomponents

* Remove commented code in test

* Change lifecycle tests per #2757

* Use errors.Is

* Remove extra lines

* Remove unneeded cast function
  • Loading branch information
pjanotti authored Mar 22, 2021
1 parent 3b13923 commit 3660f5c
Show file tree
Hide file tree
Showing 4 changed files with 584 additions and 82 deletions.
211 changes: 211 additions & 0 deletions service/defaultcomponents/default_exporters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// 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 defaultcomponents

import (
"context"
"errors"
"io/ioutil"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configerror"
"go.opentelemetry.io/collector/config/configgrpc"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/exporter/fileexporter"
"go.opentelemetry.io/collector/exporter/jaegerexporter"
"go.opentelemetry.io/collector/exporter/kafkaexporter"
"go.opentelemetry.io/collector/exporter/opencensusexporter"
"go.opentelemetry.io/collector/exporter/otlpexporter"
"go.opentelemetry.io/collector/exporter/otlphttpexporter"
"go.opentelemetry.io/collector/exporter/prometheusexporter"
"go.opentelemetry.io/collector/exporter/zipkinexporter"
"go.opentelemetry.io/collector/testutil"
)

func TestDefaultExporters(t *testing.T) {
factories, err := Components()
assert.NoError(t, err)

expFactories := factories.Exporters
endpoint := testutil.GetAvailableLocalAddress(t)

tests := []struct {
exporter configmodels.Type
getConfigFn getExporterConfigFn
}{
{
exporter: "file",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["file"].CreateDefaultConfig().(*fileexporter.Config)
f, err := ioutil.TempFile("", "otelcol_defaults_file_exporter_test*.tmp")
require.NoError(t, err)
assert.NoError(t, f.Close())
cfg.Path = f.Name()
return cfg
},
},
{
exporter: "jaeger",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["jaeger"].CreateDefaultConfig().(*jaegerexporter.Config)
cfg.Endpoint = endpoint
return cfg
},
},
{
exporter: "kafka",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["kafka"].CreateDefaultConfig().(*kafkaexporter.Config)
cfg.Brokers = []string{"invalid:9092"}
// this disables contacting the broker so we can successfully create the exporter
cfg.Metadata.Full = false
return cfg
},
},
{
exporter: "logging",
},
{
exporter: "opencensus",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["opencensus"].CreateDefaultConfig().(*opencensusexporter.Config)
cfg.GRPCClientSettings = configgrpc.GRPCClientSettings{
Endpoint: endpoint,
}
return cfg
},
},
{
exporter: "otlp",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["otlp"].CreateDefaultConfig().(*otlpexporter.Config)
cfg.GRPCClientSettings = configgrpc.GRPCClientSettings{
Endpoint: endpoint,
}
return cfg
},
},
{
exporter: "otlphttp",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["otlphttp"].CreateDefaultConfig().(*otlphttpexporter.Config)
cfg.Endpoint = "http://" + endpoint
return cfg
},
},
{
exporter: "prometheus",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["prometheus"].CreateDefaultConfig().(*prometheusexporter.Config)
cfg.Endpoint = endpoint
return cfg
},
},
{
exporter: "prometheusremotewrite",
},
{
exporter: "zipkin",
getConfigFn: func() configmodels.Exporter {
cfg := expFactories["zipkin"].CreateDefaultConfig().(*zipkinexporter.Config)
cfg.Endpoint = endpoint
return cfg
},
},
}

assert.Equal(t, len(tests), len(expFactories))
for _, tt := range tests {
t.Run(string(tt.exporter), func(t *testing.T) {
factory, ok := expFactories[tt.exporter]
require.True(t, ok)
assert.Equal(t, tt.exporter, factory.Type())
assert.Equal(t, tt.exporter, factory.CreateDefaultConfig().Type())

verifyExporterLifecycle(t, factory, tt.getConfigFn)
})
}
}

// GetExporterConfigFn is used customize the configuration passed to the verification.
// This is used to change ports or provide values required but not provided by the
// default configuration.
type getExporterConfigFn func() configmodels.Exporter

// verifyExporterLifecycle is used to test if an exporter type can handle the typical
// lifecycle of a component. The getConfigFn parameter only need to be specified if
// the test can't be done with the default configuration for the component.
func verifyExporterLifecycle(t *testing.T, factory component.ExporterFactory, getConfigFn getExporterConfigFn) {
ctx := context.Background()
host := newAssertNoErrorHost(t)
expCreateParams := component.ExporterCreateParams{
Logger: zap.NewNop(),
ApplicationStartInfo: component.DefaultApplicationStartInfo(),
}

if getConfigFn == nil {
getConfigFn = factory.CreateDefaultConfig
}

createFns := []createExporterFn{
wrapCreateLogsExp(factory),
wrapCreateTracesExp(factory),
wrapCreateMetricsExp(factory),
}

for _, createFn := range createFns {
firstExp, err := createFn(ctx, expCreateParams, getConfigFn())
if errors.Is(err, configerror.ErrDataTypeIsNotSupported) {
continue
}
require.NoError(t, err)
require.NoError(t, firstExp.Start(ctx, host))
require.NoError(t, firstExp.Shutdown(ctx))

secondExp, err := createFn(ctx, expCreateParams, getConfigFn())
require.NoError(t, err)
require.NoError(t, secondExp.Start(ctx, host))
require.NoError(t, secondExp.Shutdown(ctx))
}
}

type createExporterFn func(
ctx context.Context,
params component.ExporterCreateParams,
cfg configmodels.Exporter,
) (component.Exporter, error)

func wrapCreateLogsExp(factory component.ExporterFactory) createExporterFn {
return func(ctx context.Context, params component.ExporterCreateParams, cfg configmodels.Exporter) (component.Exporter, error) {
return factory.CreateLogsExporter(ctx, params, cfg)
}
}

func wrapCreateTracesExp(factory component.ExporterFactory) createExporterFn {
return func(ctx context.Context, params component.ExporterCreateParams, cfg configmodels.Exporter) (component.Exporter, error) {
return factory.CreateTracesExporter(ctx, params, cfg)
}
}

func wrapCreateMetricsExp(factory component.ExporterFactory) createExporterFn {
return func(ctx context.Context, params component.ExporterCreateParams, cfg configmodels.Exporter) (component.Exporter, error) {
return factory.CreateMetricsExporter(ctx, params, cfg)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Program otelcol is the OpenTelemetry Collector that collects stats
// and traces and exports to a configured backend.
package defaultcomponents

import (
Expand All @@ -29,85 +27,40 @@ import (
"go.opentelemetry.io/collector/config/configmodels"
)

func TestDefaultComponents(t *testing.T) {
expectedExtensions := []configmodels.Type{
"health_check",
"pprof",
"zpages",
"fluentbit",
}
expectedReceivers := []configmodels.Type{
"jaeger",
"zipkin",
"prometheus",
"opencensus",
"otlp",
"hostmetrics",
"fluentforward",
"kafka",
}
expectedProcessors := []configmodels.Type{
"attributes",
"resource",
"batch",
"memory_limiter",
"probabilistic_sampler",
"span",
"filter",
}
expectedExporters := []configmodels.Type{
"opencensus",
"prometheus",
"prometheusremotewrite",
"logging",
"zipkin",
"jaeger",
"file",
"otlp",
"otlphttp",
"kafka",
}

factories, err := Components()
assert.NoError(t, err)

exts := factories.Extensions
assert.Equal(t, len(expectedExtensions), len(exts))
for _, k := range expectedExtensions {
v, ok := exts[k]
assert.True(t, ok)
assert.Equal(t, k, v.Type())
cfg := v.CreateDefaultConfig()
assert.Equal(t, k, cfg.Type())

verifyExtensionLifecycle(t, v, nil)
}
func TestDefaultExtensions(t *testing.T) {
allFactories, err := Components()
require.NoError(t, err)

recvs := factories.Receivers
assert.Equal(t, len(expectedReceivers), len(recvs))
for _, k := range expectedReceivers {
v, ok := recvs[k]
require.True(t, ok)
assert.Equal(t, k, v.Type())
assert.Equal(t, k, v.CreateDefaultConfig().Type())
extFactories := allFactories.Extensions

tests := []struct {
extension configmodels.Type
getConfigFn getExtensionConfigFn
}{
{
extension: "health_check",
},
{
extension: "pprof",
},
{
extension: "zpages",
},
{
extension: "fluentbit",
},
}

procs := factories.Processors
assert.Equal(t, len(expectedProcessors), len(procs))
for _, k := range expectedProcessors {
v, ok := procs[k]
require.True(t, ok)
assert.Equal(t, k, v.Type())
assert.Equal(t, k, v.CreateDefaultConfig().Type())
}
assert.Equal(t, len(tests), len(extFactories))
for _, tt := range tests {
t.Run(string(tt.extension), func(t *testing.T) {
factory, ok := extFactories[tt.extension]
require.True(t, ok)
assert.Equal(t, tt.extension, factory.Type())
assert.Equal(t, tt.extension, factory.CreateDefaultConfig().Type())

exps := factories.Exporters
assert.Equal(t, len(expectedExporters), len(exps))
for _, k := range expectedExporters {
v, ok := exps[k]
require.True(t, ok)
assert.Equal(t, k, v.Type())
assert.Equal(t, k, v.CreateDefaultConfig().Type())
verifyExtensionLifecycle(t, factory, nil)
})
}
}

Expand All @@ -134,13 +87,12 @@ func verifyExtensionLifecycle(t *testing.T, factory component.ExtensionFactory,
firstExt, err := factory.CreateExtension(ctx, extCreateParams, getConfigFn())
require.NoError(t, err)
require.NoError(t, firstExt.Start(ctx, host))
require.NoError(t, firstExt.Shutdown(ctx))

secondExt, err := factory.CreateExtension(ctx, extCreateParams, getConfigFn())
assert.NoError(t, err)

assert.NoError(t, firstExt.Shutdown(ctx))
assert.NoError(t, secondExt.Start(ctx, host))
assert.NoError(t, secondExt.Shutdown(ctx))
require.NoError(t, err)
require.NoError(t, secondExt.Start(ctx, host))
require.NoError(t, secondExt.Shutdown(ctx))
}

// assertNoErrorHost implements a component.Host that asserts that there were no errors.
Expand Down
Loading

0 comments on commit 3660f5c

Please sign in to comment.