Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[exporter/datadog] Decouple Config structs from internal components #8375

Merged
merged 4 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions exporter/datadogexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func createMetricsExporter(
if md.ResourceMetrics().Len() > 0 {
attrs = md.ResourceMetrics().At(0).Resource().Attributes()
}
go metadata.Pusher(ctx, set, cfg, attrs)
go metadata.Pusher(ctx, set, newMetadataConfigfromConfig(cfg), attrs)
})
return nil
}
Expand Down Expand Up @@ -189,7 +189,7 @@ func createTracesExporter(
if td.ResourceSpans().Len() > 0 {
attrs = td.ResourceSpans().At(0).Resource().Attributes()
}
go metadata.Pusher(ctx, set, cfg, attrs)
go metadata.Pusher(ctx, set, newMetadataConfigfromConfig(cfg), attrs)
})
return nil
}
Expand Down
34 changes: 34 additions & 0 deletions exporter/datadogexporter/hostmetadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 datadogexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter"

import (
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata"
)

// newMetadataConfigfromConfig creates a new metadata pusher config from the main config.
func newMetadataConfigfromConfig(cfg *config.Config) metadata.PusherConfig {
return metadata.PusherConfig{
ConfigHostname: cfg.Hostname,
ConfigTags: cfg.GetHostTags(),
MetricsEndpoint: cfg.Metrics.Endpoint,
APIKey: cfg.API.Key,
UseResourceMetadata: cfg.UseResourceMetadata,
InsecureSkipVerify: cfg.TLSSetting.InsecureSkipVerify,
TimeoutSettings: cfg.TimeoutSettings,
RetrySettings: cfg.RetrySettings,
}
}
39 changes: 39 additions & 0 deletions exporter/datadogexporter/internal/metadata/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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 metadata // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata"

import (
"go.opentelemetry.io/collector/exporter/exporterhelper"
)

// PusherConfig is the configuration for the metadata pusher goroutine.
type PusherConfig struct {
// ConfigHosthame is the hostname set in the configuration of the exporter (empty if unset).
ConfigHostname string
// ConfigTags are the tags set in the configuration of the exporter (empty if unset).
ConfigTags []string
// MetricsEndpoint is the metrics endpoint.
MetricsEndpoint string
// APIKey is the API key set in configuration.
APIKey string
// UseResourceMetadata is the value of 'use_resource_metadata' on the top-level configuration.
UseResourceMetadata bool
// InsecureSkipVerify is the value of `tls.insecure_skip_verify` on the configuration.
InsecureSkipVerify bool
// TimeoutSettings of exporter.
TimeoutSettings exporterhelper.TimeoutSettings
// RetrySettings of exporter.
RetrySettings exporterhelper.RetrySettings
}
7 changes: 3 additions & 4 deletions exporter/datadogexporter/internal/metadata/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package metadata // import "github.com/open-telemetry/opentelemetry-collector-co
import (
"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata/ec2"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata/system"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata/valid"
Expand All @@ -30,9 +29,9 @@ import (
// 2. Cache
// 3. EC2 instance metadata
// 4. System
func GetHost(logger *zap.Logger, cfg *config.Config) string {
if cfg.Hostname != "" {
return cfg.Hostname
func GetHost(logger *zap.Logger, configHostname string) string {
if configHostname != "" {
return configHostname
}

if cacheVal, ok := cache.Cache.Get(cache.CanonicalHostnameKey); ok {
Expand Down
11 changes: 3 additions & 8 deletions exporter/datadogexporter/internal/metadata/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/utils/cache"
)

Expand All @@ -34,15 +33,11 @@ func TestHost(t *testing.T) {
// if the cache key is already set.
cache.Cache.Delete(cache.CanonicalHostnameKey)

host := GetHost(logger, &config.Config{
TagsConfig: config.TagsConfig{Hostname: "test-host"},
})
host := GetHost(logger, "test-host")
assert.Equal(t, host, "test-host")

// config.Config.Hostname does not get stored in the cache
host = GetHost(logger, &config.Config{
TagsConfig: config.TagsConfig{Hostname: "test-host-2"},
})
host = GetHost(logger, "test-host-2")
assert.Equal(t, host, "test-host-2")

// Disable EC2 Metadata service to prevent fetching hostname from there,
Expand All @@ -53,7 +48,7 @@ func TestHost(t *testing.T) {
require.NoError(t, err)
defer os.Setenv(awsEc2MetadataDisabled, curr)

host = GetHost(logger, &config.Config{})
host = GetHost(logger, "")
osHostname, err := os.Hostname()
require.NoError(t, err)
// TODO: Investigate why the returned host contains more data on github actions.
Expand Down
31 changes: 15 additions & 16 deletions exporter/datadogexporter/internal/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
conventions "go.opentelemetry.io/collector/model/semconv/v1.6.1"
"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata/ec2"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata/system"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/model/attributes"
Expand Down Expand Up @@ -120,10 +119,10 @@ func metadataFromAttributes(attrs pdata.AttributeMap) *HostMetadata {
return hm
}

func fillHostMetadata(params component.ExporterCreateSettings, cfg *config.Config, hm *HostMetadata) {
func fillHostMetadata(params component.ExporterCreateSettings, pcfg PusherConfig, hm *HostMetadata) {
// Could not get hostname from attributes
if hm.InternalHostname == "" {
hostname := GetHost(params.Logger, cfg)
hostname := GetHost(params.Logger, pcfg.ConfigHostname)
hm.InternalHostname = hostname
hm.Meta.Hostname = hostname
}
Expand All @@ -132,7 +131,7 @@ func fillHostMetadata(params component.ExporterCreateSettings, cfg *config.Confi
// since it does not come from OTEL conventions
hm.Flavor = params.BuildInfo.Command
hm.Version = params.BuildInfo.Version
hm.Tags.OTel = append(hm.Tags.OTel, cfg.GetHostTags()...)
hm.Tags.OTel = append(hm.Tags.OTel, pcfg.ConfigTags...)

// EC2 data was not set from attributes
if hm.Meta.EC2Hostname == "" {
Expand All @@ -149,19 +148,19 @@ func fillHostMetadata(params component.ExporterCreateSettings, cfg *config.Confi
}
}

func pushMetadata(cfg *config.Config, params component.ExporterCreateSettings, metadata *HostMetadata) error {
func pushMetadata(pcfg PusherConfig, params component.ExporterCreateSettings, metadata *HostMetadata) error {
if metadata.Meta.Hostname == "" {
// if the hostname is empty, don't send metadata; we don't need it.
params.Logger.Debug("Skipping host metadata since the hostname is empty")
return nil
}

path := cfg.Metrics.TCPAddr.Endpoint + "/intake"
path := pcfg.MetricsEndpoint + "/intake"
buf, _ := json.Marshal(metadata)
req, _ := http.NewRequest(http.MethodPost, path, bytes.NewBuffer(buf))
utils.SetDDHeaders(req.Header, params.BuildInfo, cfg.API.Key)
utils.SetDDHeaders(req.Header, params.BuildInfo, pcfg.APIKey)
utils.SetExtraHeaders(req.Header, utils.JSONHeaders)
client := utils.NewHTTPClient(cfg.TimeoutSettings, cfg.LimitedHTTPClientSettings)
client := utils.NewHTTPClient(pcfg.TimeoutSettings, pcfg.InsecureSkipVerify)
resp, err := client.Do(req)

if err != nil {
Expand All @@ -181,11 +180,11 @@ func pushMetadata(cfg *config.Config, params component.ExporterCreateSettings, m
return nil
}

func pushMetadataWithRetry(retrier *utils.Retrier, params component.ExporterCreateSettings, cfg *config.Config, hostMetadata *HostMetadata) {
func pushMetadataWithRetry(retrier *utils.Retrier, params component.ExporterCreateSettings, pcfg PusherConfig, hostMetadata *HostMetadata) {
params.Logger.Debug("Sending host metadata payload", zap.Any("payload", hostMetadata))

err := retrier.DoWithRetries(context.Background(), func(context.Context) error {
return pushMetadata(cfg, params, hostMetadata)
return pushMetadata(pcfg, params, hostMetadata)
})

if err != nil {
Expand All @@ -197,12 +196,12 @@ func pushMetadataWithRetry(retrier *utils.Retrier, params component.ExporterCrea
}

// Pusher pushes host metadata payloads periodically to Datadog intake
func Pusher(ctx context.Context, params component.ExporterCreateSettings, cfg *config.Config, attrs pdata.AttributeMap) {
func Pusher(ctx context.Context, params component.ExporterCreateSettings, pcfg PusherConfig, attrs pdata.AttributeMap) {
// Push metadata every 30 minutes
ticker := time.NewTicker(30 * time.Minute)
defer ticker.Stop()
defer params.Logger.Debug("Shut down host metadata routine")
retrier := utils.NewRetrier(params.Logger, cfg.RetrySettings, scrub.NewScrubber())
retrier := utils.NewRetrier(params.Logger, pcfg.RetrySettings, scrub.NewScrubber())

// Get host metadata from resources and fill missing info using our exporter.
// Currently we only retrieve it once but still send the same payload
Expand All @@ -212,20 +211,20 @@ func Pusher(ctx context.Context, params component.ExporterCreateSettings, cfg *c
// do not change over time. If this ever changes `hostMetadata`
// *must* be deep copied before calling `fillHostMetadata`.
hostMetadata := &HostMetadata{Meta: &Meta{}, Tags: &HostTags{}}
if cfg.UseResourceMetadata {
if pcfg.UseResourceMetadata {
hostMetadata = metadataFromAttributes(attrs)
}
fillHostMetadata(params, cfg, hostMetadata)
fillHostMetadata(params, pcfg, hostMetadata)

// Run one first time at startup
pushMetadataWithRetry(retrier, params, cfg, hostMetadata)
pushMetadataWithRetry(retrier, params, pcfg, hostMetadata)

for {
select {
case <-ctx.Done():
return
case <-ticker.C: // Send host metadata
pushMetadataWithRetry(retrier, params, cfg, hostMetadata)
pushMetadataWithRetry(retrier, params, pcfg, hostMetadata)
}
}
}
39 changes: 20 additions & 19 deletions exporter/datadogexporter/internal/metadata/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (
"go.opentelemetry.io/collector/component/componenttest"
conventions "go.opentelemetry.io/collector/model/semconv/v1.6.1"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/model/attributes"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/model/attributes/azure"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/testutils"
Expand Down Expand Up @@ -67,14 +66,13 @@ func TestFillHostMetadata(t *testing.T) {
params := componenttest.NewNopExporterCreateSettings()
params.BuildInfo = mockBuildInfo

cfg := &config.Config{TagsConfig: config.TagsConfig{
Hostname: "hostname",
Env: "prod",
Tags: []string{"key1:tag1", "key2:tag2"},
}}
pcfg := PusherConfig{
ConfigHostname: "hostname",
ConfigTags: []string{"key1:tag1", "key2:tag2", "env:prod"},
}

metadata := &HostMetadata{Meta: &Meta{}, Tags: &HostTags{}}
fillHostMetadata(params, cfg, metadata)
fillHostMetadata(params, pcfg, metadata)

assert.Equal(t, metadata.InternalHostname, "hostname")
assert.Equal(t, metadata.Flavor, "otelcontribcol")
Expand All @@ -88,7 +86,7 @@ func TestFillHostMetadata(t *testing.T) {
Tags: &HostTags{},
}

fillHostMetadata(params, cfg, metadataWithVals)
fillHostMetadata(params, pcfg, metadataWithVals)
assert.Equal(t, metadataWithVals.InternalHostname, "my-custom-hostname")
assert.Equal(t, metadataWithVals.Flavor, "otelcontribcol")
assert.Equal(t, metadataWithVals.Version, "1.0")
Expand Down Expand Up @@ -156,7 +154,9 @@ func TestMetadataFromAttributes(t *testing.T) {
}

func TestPushMetadata(t *testing.T) {
cfg := &config.Config{API: config.APIConfig{Key: "apikey"}}
pcfg := PusherConfig{
APIKey: "apikey",
}

handler := http.NewServeMux()
handler.HandleFunc("/intake", func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -174,29 +174,30 @@ func TestPushMetadata(t *testing.T) {

ts := httptest.NewServer(handler)
defer ts.Close()
cfg.Metrics.Endpoint = ts.URL
pcfg.MetricsEndpoint = ts.URL

err := pushMetadata(cfg, mockExporterCreateSettings, &mockMetadata)
err := pushMetadata(pcfg, mockExporterCreateSettings, &mockMetadata)
require.NoError(t, err)
}

func TestFailPushMetadata(t *testing.T) {
cfg := &config.Config{API: config.APIConfig{Key: "apikey"}}

pcfg := PusherConfig{
APIKey: "apikey",
}
handler := http.NewServeMux()
handler.Handle("/intake", http.NotFoundHandler())

ts := httptest.NewServer(handler)
defer ts.Close()
cfg.Metrics.Endpoint = ts.URL
pcfg.MetricsEndpoint = ts.URL

err := pushMetadata(cfg, mockExporterCreateSettings, &mockMetadata)
err := pushMetadata(pcfg, mockExporterCreateSettings, &mockMetadata)
require.Error(t, err)
}

func TestPusher(t *testing.T) {
cfg := &config.Config{
API: config.APIConfig{Key: "apikey"},
pcfg := PusherConfig{
APIKey: "apikey",
UseResourceMetadata: true,
}
params := componenttest.NewNopExporterCreateSettings()
Expand All @@ -210,9 +211,9 @@ func TestPusher(t *testing.T) {

server := testutils.DatadogServerMock()
defer server.Close()
cfg.Metrics.Endpoint = server.URL
pcfg.MetricsEndpoint = server.URL

go Pusher(ctx, params, cfg, attrs)
go Pusher(ctx, params, pcfg, attrs)

body := <-server.MetadataChan
var recvMetadata HostMetadata
Expand Down
4 changes: 1 addition & 3 deletions exporter/datadogexporter/internal/metrics/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (

"go.opentelemetry.io/collector/component"
"gopkg.in/zorkian/go-datadog-api.v2"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config"
)

type MetricDataType string
Expand Down Expand Up @@ -94,7 +92,7 @@ func DefaultMetrics(exporterType string, hostname string, timestamp uint64, buil

// ProcessMetrics adds the hostname to the metric and prefixes it with the "otel"
// namespace as the Datadog backend expects
func ProcessMetrics(ms []datadog.Metric, cfg *config.Config) {
func ProcessMetrics(ms []datadog.Metric) {
addNamespace(ms, otelNamespacePrefix)
}

Expand Down
Loading