123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 |
- // Copyright The OpenTelemetry Authors
- // SPDX-License-Identifier: Apache-2.0
- package datadogexporter
- import (
- "context"
- "encoding/json"
- "path/filepath"
- "sync"
- "testing"
- "github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata"
- "github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/payload"
- "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/confignet"
- "go.opentelemetry.io/collector/confmap/confmaptest"
- "go.opentelemetry.io/collector/exporter/exporterhelper"
- "go.opentelemetry.io/collector/exporter/exportertest"
- "go.opentelemetry.io/collector/pdata/ptrace"
- "go.uber.org/zap"
- "go.uber.org/zap/zaptest"
- "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata"
- "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/testutil"
- )
- var _ inframetadata.Pusher = (*testPusher)(nil)
- type testPusher struct {
- mu sync.Mutex
- payloads []payload.HostMetadata
- stopped bool
- logger *zap.Logger
- t *testing.T
- }
- func newTestPusher(t *testing.T) *testPusher {
- return &testPusher{
- logger: zaptest.NewLogger(t),
- t: t,
- }
- }
- func (p *testPusher) Push(_ context.Context, hm payload.HostMetadata) error {
- p.mu.Lock()
- defer p.mu.Unlock()
- if p.stopped {
- p.logger.Error("Trying to push payload after stopping", zap.Any("payload", hm))
- p.t.Fail()
- }
- p.logger.Info("Storing host metadata payload", zap.Any("payload", hm))
- p.payloads = append(p.payloads, hm)
- return nil
- }
- func (p *testPusher) Payloads() []payload.HostMetadata {
- p.mu.Lock()
- p.stopped = true
- defer p.mu.Unlock()
- return p.payloads
- }
- // Test that the factory creates the default configuration
- func TestCreateDefaultConfig(t *testing.T) {
- factory := NewFactory()
- cfg := factory.CreateDefaultConfig()
- assert.Equal(t, &Config{
- TimeoutSettings: defaulttimeoutSettings(),
- RetrySettings: exporterhelper.NewDefaultRetrySettings(),
- QueueSettings: exporterhelper.NewDefaultQueueSettings(),
- API: APIConfig{
- Site: "datadoghq.com",
- },
- Metrics: MetricsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://api.datadoghq.com",
- },
- DeltaTTL: 3600,
- HistConfig: HistogramConfig{
- Mode: "distributions",
- SendAggregations: false,
- },
- SumConfig: SumConfig{
- CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta,
- InitialCumulativeMonotonicMode: InitialValueModeAuto,
- },
- SummaryConfig: SummaryConfig{
- Mode: SummaryModeGauges,
- },
- },
- Traces: TracesConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://trace.agent.datadoghq.com",
- },
- IgnoreResources: []string{},
- },
- Logs: LogsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://http-intake.logs.datadoghq.com",
- },
- },
- HostMetadata: HostMetadataConfig{
- Enabled: true,
- HostnameSource: HostnameSourceConfigOrSystem,
- },
- OnlyMetadata: false,
- }, cfg, "failed to create default config")
- assert.NoError(t, componenttest.CheckConfigStruct(cfg))
- }
- func TestLoadConfig(t *testing.T) {
- t.Parallel()
- cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
- require.NoError(t, err)
- tests := []struct {
- id component.ID
- expected component.Config
- }{
- {
- id: component.NewIDWithName(metadata.Type, "default"),
- expected: &Config{
- TimeoutSettings: defaulttimeoutSettings(),
- RetrySettings: exporterhelper.NewDefaultRetrySettings(),
- QueueSettings: exporterhelper.NewDefaultQueueSettings(),
- API: APIConfig{
- Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- Site: "datadoghq.com",
- FailOnInvalidKey: false,
- },
- Metrics: MetricsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://api.datadoghq.com",
- },
- DeltaTTL: 3600,
- HistConfig: HistogramConfig{
- Mode: "distributions",
- SendAggregations: false,
- },
- SumConfig: SumConfig{
- CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta,
- InitialCumulativeMonotonicMode: InitialValueModeAuto,
- },
- SummaryConfig: SummaryConfig{
- Mode: SummaryModeGauges,
- },
- },
- Traces: TracesConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://trace.agent.datadoghq.com",
- },
- IgnoreResources: []string{},
- },
- Logs: LogsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://http-intake.logs.datadoghq.com",
- },
- },
- HostMetadata: HostMetadataConfig{
- Enabled: true,
- HostnameSource: HostnameSourceConfigOrSystem,
- },
- OnlyMetadata: false,
- },
- },
- {
- id: component.NewIDWithName(metadata.Type, "api"),
- expected: &Config{
- TimeoutSettings: defaulttimeoutSettings(),
- RetrySettings: exporterhelper.NewDefaultRetrySettings(),
- QueueSettings: exporterhelper.NewDefaultQueueSettings(),
- TagsConfig: TagsConfig{
- Hostname: "customhostname",
- },
- API: APIConfig{
- Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- Site: "datadoghq.eu",
- FailOnInvalidKey: true,
- },
- Metrics: MetricsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://api.datadoghq.eu",
- },
- DeltaTTL: 3600,
- HistConfig: HistogramConfig{
- Mode: "distributions",
- SendAggregations: false,
- },
- SumConfig: SumConfig{
- CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta,
- InitialCumulativeMonotonicMode: InitialValueModeAuto,
- },
- SummaryConfig: SummaryConfig{
- Mode: SummaryModeGauges,
- },
- },
- Traces: TracesConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://trace.agent.datadoghq.eu",
- },
- SpanNameRemappings: map[string]string{
- "old_name1": "new_name1",
- "old_name2": "new_name2",
- },
- SpanNameAsResourceName: true,
- IgnoreResources: []string{},
- TraceBuffer: 10,
- },
- Logs: LogsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://http-intake.logs.datadoghq.eu",
- },
- },
- OnlyMetadata: false,
- HostMetadata: HostMetadataConfig{
- Enabled: true,
- HostnameSource: HostnameSourceConfigOrSystem,
- },
- },
- },
- {
- id: component.NewIDWithName(metadata.Type, "api2"),
- expected: &Config{
- TimeoutSettings: defaulttimeoutSettings(),
- RetrySettings: exporterhelper.NewDefaultRetrySettings(),
- QueueSettings: exporterhelper.NewDefaultQueueSettings(),
- TagsConfig: TagsConfig{
- Hostname: "customhostname",
- },
- API: APIConfig{
- Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- Site: "datadoghq.eu",
- FailOnInvalidKey: false,
- },
- Metrics: MetricsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://api.datadoghq.test",
- },
- DeltaTTL: 3600,
- HistConfig: HistogramConfig{
- Mode: "distributions",
- SendAggregations: false,
- },
- SumConfig: SumConfig{
- CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta,
- InitialCumulativeMonotonicMode: InitialValueModeAuto,
- },
- SummaryConfig: SummaryConfig{
- Mode: SummaryModeGauges,
- },
- },
- Traces: TracesConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://trace.agent.datadoghq.test",
- },
- SpanNameRemappings: map[string]string{
- "old_name3": "new_name3",
- "old_name4": "new_name4",
- },
- IgnoreResources: []string{},
- },
- Logs: LogsConfig{
- TCPAddr: confignet.TCPAddr{
- Endpoint: "https://http-intake.logs.datadoghq.test",
- },
- },
- HostMetadata: HostMetadataConfig{
- Enabled: true,
- HostnameSource: HostnameSourceConfigOrSystem,
- Tags: []string{"example:tag"},
- },
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.id.String(), func(t *testing.T) {
- factory := NewFactory()
- cfg := factory.CreateDefaultConfig()
- sub, err := cm.Sub(tt.id.String())
- require.NoError(t, err)
- require.NoError(t, component.UnmarshalConfig(sub, cfg))
- assert.NoError(t, component.ValidateConfig(cfg))
- assert.Equal(t, tt.expected, cfg)
- })
- }
- }
- func TestOverrideEndpoints(t *testing.T) {
- tests := []struct {
- componentID string
- expectedSite string
- expectedMetricsEndpoint string
- expectedTracesEndpoint string
- expectedLogsEndpoint string
- }{
- {
- componentID: "nositeandnoendpoints",
- expectedSite: "datadoghq.com",
- expectedMetricsEndpoint: "https://api.datadoghq.com",
- expectedTracesEndpoint: "https://trace.agent.datadoghq.com",
- expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com",
- },
- {
- componentID: "nositeandmetricsendpoint",
- expectedSite: "datadoghq.com",
- expectedMetricsEndpoint: "metricsendpoint:1234",
- expectedTracesEndpoint: "https://trace.agent.datadoghq.com",
- expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com",
- },
- {
- componentID: "nositeandtracesendpoint",
- expectedSite: "datadoghq.com",
- expectedMetricsEndpoint: "https://api.datadoghq.com",
- expectedTracesEndpoint: "tracesendpoint:1234",
- expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com",
- },
- {
- componentID: "nositeandlogsendpoint",
- expectedSite: "datadoghq.com",
- expectedMetricsEndpoint: "https://api.datadoghq.com",
- expectedTracesEndpoint: "https://trace.agent.datadoghq.com",
- expectedLogsEndpoint: "logsendpoint:1234",
- },
- {
- componentID: "nositeandallendpoints",
- expectedSite: "datadoghq.com",
- expectedMetricsEndpoint: "metricsendpoint:1234",
- expectedTracesEndpoint: "tracesendpoint:1234",
- expectedLogsEndpoint: "logsendpoint:1234",
- },
- {
- componentID: "siteandnoendpoints",
- expectedSite: "datadoghq.eu",
- expectedMetricsEndpoint: "https://api.datadoghq.eu",
- expectedTracesEndpoint: "https://trace.agent.datadoghq.eu",
- expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu",
- },
- {
- componentID: "siteandmetricsendpoint",
- expectedSite: "datadoghq.eu",
- expectedMetricsEndpoint: "metricsendpoint:1234",
- expectedTracesEndpoint: "https://trace.agent.datadoghq.eu",
- expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu",
- },
- {
- componentID: "siteandtracesendpoint",
- expectedSite: "datadoghq.eu",
- expectedMetricsEndpoint: "https://api.datadoghq.eu",
- expectedTracesEndpoint: "tracesendpoint:1234",
- expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu",
- },
- {
- componentID: "siteandallendpoints",
- expectedSite: "datadoghq.eu",
- expectedMetricsEndpoint: "metricsendpoint:1234",
- expectedTracesEndpoint: "tracesendpoint:1234",
- expectedLogsEndpoint: "logsendpoint:1234",
- },
- }
- cm, err := confmaptest.LoadConf(filepath.Join("testdata", "unmarshal.yaml"))
- require.NoError(t, err)
- factory := NewFactory()
- for _, testInstance := range tests {
- t.Run(testInstance.componentID, func(t *testing.T) {
- cfg := factory.CreateDefaultConfig()
- sub, err := cm.Sub(component.NewIDWithName(metadata.Type, testInstance.componentID).String())
- require.NoError(t, err)
- require.NoError(t, component.UnmarshalConfig(sub, cfg))
- componentCfg, ok := cfg.(*Config)
- require.True(t, ok, "component.Config is not a Datadog exporter config (wrong ID?)")
- assert.Equal(t, testInstance.expectedSite, componentCfg.API.Site)
- assert.Equal(t, testInstance.expectedMetricsEndpoint, componentCfg.Metrics.Endpoint)
- assert.Equal(t, testInstance.expectedTracesEndpoint, componentCfg.Traces.Endpoint)
- assert.Equal(t, testInstance.expectedLogsEndpoint, componentCfg.Logs.Endpoint)
- })
- }
- }
- func TestCreateAPIMetricsExporter(t *testing.T) {
- server := testutil.DatadogServerMock()
- defer server.Close()
- cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
- require.NoError(t, err)
- factory := NewFactory()
- cfg := factory.CreateDefaultConfig()
- sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "api").String())
- require.NoError(t, err)
- require.NoError(t, component.UnmarshalConfig(sub, cfg))
- c := cfg.(*Config)
- c.Metrics.TCPAddr.Endpoint = server.URL
- c.HostMetadata.Enabled = false
- ctx := context.Background()
- exp, err := factory.CreateMetricsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.NoError(t, err)
- assert.NotNil(t, exp)
- }
- func TestCreateAPIExporterFailOnInvalidKey_Zorkian(t *testing.T) {
- server := testutil.DatadogServerMock(testutil.ValidateAPIKeyEndpointInvalid)
- defer server.Close()
- if isMetricExportV2Enabled() {
- require.NoError(t, enableZorkianMetricExport())
- defer require.NoError(t, enableNativeMetricExport())
- }
- cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
- require.NoError(t, err)
- factory := NewFactory()
- cfg := factory.CreateDefaultConfig()
- sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "api").String())
- require.NoError(t, err)
- require.NoError(t, component.UnmarshalConfig(sub, cfg))
- // Use the mock server for API key validation
- c := cfg.(*Config)
- c.Metrics.TCPAddr.Endpoint = server.URL
- c.HostMetadata.Enabled = false
- t.Run("true", func(t *testing.T) {
- c.API.FailOnInvalidKey = true
- ctx := context.Background()
- // metrics exporter
- mexp, err := factory.CreateMetricsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.EqualError(t, err, "API Key validation failed")
- assert.Nil(t, mexp)
- texp, err := factory.CreateTracesExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.EqualError(t, err, "API Key validation failed")
- assert.Nil(t, texp)
- lexp, err := factory.CreateLogsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.EqualError(t, err, "API Key validation failed")
- assert.Nil(t, lexp)
- })
- t.Run("false", func(t *testing.T) {
- c.API.FailOnInvalidKey = false
- ctx := context.Background()
- exp, err := factory.CreateMetricsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.Nil(t, err)
- assert.NotNil(t, exp)
- texp, err := factory.CreateTracesExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.Nil(t, err)
- assert.NotNil(t, texp)
- lexp, err := factory.CreateLogsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.Nil(t, err)
- assert.NotNil(t, lexp)
- })
- }
- func TestCreateAPIExporterFailOnInvalidKey(t *testing.T) {
- server := testutil.DatadogServerMock(testutil.ValidateAPIKeyEndpointInvalid)
- defer server.Close()
- if !isMetricExportV2Enabled() {
- require.NoError(t, enableNativeMetricExport())
- defer require.NoError(t, enableZorkianMetricExport())
- }
- cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
- require.NoError(t, err)
- factory := NewFactory()
- cfg := factory.CreateDefaultConfig()
- sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "api").String())
- require.NoError(t, err)
- require.NoError(t, component.UnmarshalConfig(sub, cfg))
- // Use the mock server for API key validation
- c := cfg.(*Config)
- c.Metrics.TCPAddr.Endpoint = server.URL
- c.HostMetadata.Enabled = false
- t.Run("true", func(t *testing.T) {
- c.API.FailOnInvalidKey = true
- ctx := context.Background()
- // metrics exporter
- mexp, err := factory.CreateMetricsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.EqualError(t, err, "API Key validation failed")
- assert.Nil(t, mexp)
- texp, err := factory.CreateTracesExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.EqualError(t, err, "API Key validation failed")
- assert.Nil(t, texp)
- lexp, err := factory.CreateLogsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.EqualError(t, err, "API Key validation failed")
- assert.Nil(t, lexp)
- })
- t.Run("false", func(t *testing.T) {
- c.API.FailOnInvalidKey = false
- ctx := context.Background()
- exp, err := factory.CreateMetricsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.Nil(t, err)
- assert.NotNil(t, exp)
- texp, err := factory.CreateTracesExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.Nil(t, err)
- assert.NotNil(t, texp)
- lexp, err := factory.CreateLogsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.Nil(t, err)
- assert.NotNil(t, lexp)
- })
- }
- func TestCreateAPILogsExporter(t *testing.T) {
- server := testutil.DatadogLogServerMock()
- defer server.Close()
- cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
- require.NoError(t, err)
- factory := NewFactory()
- cfg := factory.CreateDefaultConfig()
- sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "api").String())
- require.NoError(t, err)
- require.NoError(t, component.UnmarshalConfig(sub, cfg))
- c := cfg.(*Config)
- c.Metrics.TCPAddr.Endpoint = server.URL
- c.HostMetadata.Enabled = false
- ctx := context.Background()
- exp, err := factory.CreateLogsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.NoError(t, err)
- assert.NotNil(t, exp)
- }
- func TestOnlyMetadata(t *testing.T) {
- server := testutil.DatadogServerMock()
- defer server.Close()
- factory := NewFactory()
- ctx := context.Background()
- cfg := &Config{
- TimeoutSettings: defaulttimeoutSettings(),
- RetrySettings: exporterhelper.NewDefaultRetrySettings(),
- QueueSettings: exporterhelper.NewDefaultQueueSettings(),
- API: APIConfig{Key: "notnull"},
- Metrics: MetricsConfig{TCPAddr: confignet.TCPAddr{Endpoint: server.URL}},
- Traces: TracesConfig{TCPAddr: confignet.TCPAddr{Endpoint: server.URL}},
- OnlyMetadata: true,
- HostMetadata: HostMetadataConfig{
- Enabled: true,
- HostnameSource: HostnameSourceFirstResource,
- },
- }
- expTraces, err := factory.CreateTracesExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.NoError(t, err)
- assert.NotNil(t, expTraces)
- expMetrics, err := factory.CreateMetricsExporter(
- ctx,
- exportertest.NewNopCreateSettings(),
- cfg,
- )
- assert.NoError(t, err)
- assert.NotNil(t, expMetrics)
- err = expTraces.Start(ctx, nil)
- assert.NoError(t, err)
- defer func() {
- assert.NoError(t, expTraces.Shutdown(ctx))
- }()
- testTraces := ptrace.NewTraces()
- testutil.TestTraces.CopyTo(testTraces)
- err = expTraces.ConsumeTraces(ctx, testTraces)
- require.NoError(t, err)
- body := <-server.MetadataChan
- var recvMetadata payload.HostMetadata
- err = json.Unmarshal(body, &recvMetadata)
- require.NoError(t, err)
- assert.Equal(t, recvMetadata.InternalHostname, "custom-hostname")
- }
|