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

[pkg/otlp] Backport changes from Datadog exporter #12452

Merged
merged 6 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
20 changes: 13 additions & 7 deletions pkg/otlp/internal/serializerexporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"context"
"fmt"

"github.com/DataDog/datadog-agent/pkg/otlp/model/source"
"github.com/DataDog/datadog-agent/pkg/otlp/model/translator"
"github.com/DataDog/datadog-agent/pkg/serializer"
"github.com/DataDog/datadog-agent/pkg/tagger/collectors"
Expand Down Expand Up @@ -51,14 +52,19 @@ func newDefaultConfig() config.Exporter {
}
}

var _ translator.HostnameProvider = (*hostnameProviderFunc)(nil)
var _ source.Provider = (*sourceProviderFunc)(nil)

// hostnameProviderFunc is an adapter to allow the use of a function as a translator.HostnameProvider.
type hostnameProviderFunc func(context.Context) (string, error)
// sourceProviderFunc is an adapter to allow the use of a function as a translator.HostnameProvider.
type sourceProviderFunc func(context.Context) (string, error)

// Hostname calls f.
func (f hostnameProviderFunc) Hostname(ctx context.Context) (string, error) {
return f(ctx)
// Source calls f and wraps in a source struct.
func (f sourceProviderFunc) Source(ctx context.Context) (source.Source, error) {
hostname, err := f(ctx)
if err != nil {
return source.Source{}, err
}

return source.Source{Kind: source.HostnameKind, Identifier: hostname}, nil
}

// exporter translate OTLP metrics into the Datadog format and sends
Expand All @@ -81,7 +87,7 @@ func translatorFromConfig(logger *zap.Logger, cfg *exporterConfig) (*translator.
}

options := []translator.Option{
translator.WithFallbackHostnameProvider(hostnameProviderFunc(util.GetHostname)),
translator.WithFallbackSourceProvider(sourceProviderFunc(util.GetHostname)),
translator.WithHistogramMode(histogramMode),
translator.WithDeltaTTL(cfg.Metrics.DeltaTTL),
}
Expand Down
17 changes: 0 additions & 17 deletions pkg/otlp/model/attributes/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,6 @@ var (
conventions.AttributeAWSECSContainerARN,
}

runningTagsAttributes = []string{
conventions.AttributeAWSECSTaskARN,
}

// Kubernetes mappings defines the mapping between Kubernetes conventions (both general and Datadog specific)
// and Datadog Agent conventions. The Datadog Agent conventions can be found at
// https://github.com/DataDog/datadog-agent/blob/e081bed/pkg/tagger/collectors/const.go and
Expand Down Expand Up @@ -176,19 +172,6 @@ func OriginIDFromAttributes(attrs pcommon.Map) (originID string) {
return
}

// RunningTagsFromAttributes gets tags used for running metrics from attributes.
func RunningTagsFromAttributes(attrs pcommon.Map) []string {
tags := make([]string, 0, 1)
for _, key := range runningTagsAttributes {
if val, ok := attrs.Get(key); ok {
if ddKey, found := conventionsMapping[key]; found && val.StringVal() != "" {
tags = append(tags, fmt.Sprintf("%s:%s", ddKey, val.StringVal()))
}
}
}
return tags
}

// ContainerTagFromAttributes extracts the value of _dd.tags.container from the given
// set of attributes.
func ContainerTagFromAttributes(attr map[string]string) string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/DataDog/datadog-agent/pkg/otlp/model/attributes/azure"
"github.com/DataDog/datadog-agent/pkg/otlp/model/attributes/ec2"
"github.com/DataDog/datadog-agent/pkg/otlp/model/attributes/gcp"
"github.com/DataDog/datadog-agent/pkg/otlp/model/source"
)

const (
Expand Down Expand Up @@ -55,7 +56,7 @@ func getClusterName(attrs pcommon.Map) (string, bool) {
// 6. the host.name attribute.
//
// It returns a boolean value indicated if any name was found
func HostnameFromAttributes(attrs pcommon.Map, usePreviewRules bool) (string, bool) {
func hostnameFromAttributes(attrs pcommon.Map, usePreviewRules bool) (string, bool) {
// Check if the host is localhost or 0.0.0.0, if so discard it.
// We don't do the more strict validation done for metadata,
// to avoid breaking users existing invalid-but-accepted hostnames.
Expand All @@ -75,23 +76,33 @@ func HostnameFromAttributes(attrs pcommon.Map, usePreviewRules bool) (string, bo
return candidateHost, ok
}

func k8sHostnameFromAttributes(attrs pcommon.Map) (string, bool) {
if k8sNodeName, ok := attrs.Get(AttributeK8sNodeName); ok {
mx-psi marked this conversation as resolved.
Show resolved Hide resolved
if k8sClusterName, ok := getClusterName(attrs); ok {
return k8sNodeName.StringVal() + "-" + k8sClusterName, true
}
return k8sNodeName.StringVal(), true
}

return "", false
}

func unsanitizedHostnameFromAttributes(attrs pcommon.Map, usePreviewRules bool) (string, bool) {
// Custom hostname: useful for overriding in k8s/cloud envs
if customHostname, ok := attrs.Get(AttributeDatadogHostname); ok {
return customHostname.StringVal(), true
}

if launchType, ok := attrs.Get(conventions.AttributeAWSECSLaunchtype); ok && launchType.StringVal() == conventions.AttributeAWSECSLaunchtypeFargate {
// If on AWS ECS Fargate, return a valid but empty hostname
return "", true
// If on AWS ECS Fargate, we don't have a hostname
return "", false
}

// Kubernetes: node-cluster if cluster name is available, else node
if k8sNodeName, ok := attrs.Get(AttributeK8sNodeName); ok {
if k8sClusterName, ok := getClusterName(attrs); ok {
return k8sNodeName.StringVal() + "-" + k8sClusterName, true
}
return k8sNodeName.StringVal(), true
k8sName, k8sOk := k8sHostnameFromAttributes(attrs)

if !usePreviewRules && k8sOk {
mx-psi marked this conversation as resolved.
Show resolved Hide resolved
return k8sName, true
}

cloudProvider, ok := attrs.Get(conventions.AttributeCloudProvider)
Expand All @@ -103,6 +114,10 @@ func unsanitizedHostnameFromAttributes(attrs pcommon.Map, usePreviewRules bool)
return azure.HostnameFromAttributes(attrs, usePreviewRules)
}

if usePreviewRules && k8sOk {
return k8sName, true
}

// host id from cloud provider
if hostID, ok := attrs.Get(conventions.AttributeHostID); ok {
return hostID.StringVal(), true
Expand All @@ -122,3 +137,18 @@ func unsanitizedHostnameFromAttributes(attrs pcommon.Map, usePreviewRules bool)

return "", false
}

// SourceFromAttributes gets a telemetry signal source from its attributes.
func SourceFromAttributes(attrs pcommon.Map, usePreviewRules bool) (source.Source, bool) {
if launchType, ok := attrs.Get(conventions.AttributeAWSECSLaunchtype); ok && launchType.StringVal() == conventions.AttributeAWSECSLaunchtypeFargate {
if taskARN, ok := attrs.Get(conventions.AttributeAWSECSTaskARN); ok {
return source.Source{Kind: source.AWSECSFargateKind, Identifier: taskARN.StringVal()}, true
}
}

if host, ok := hostnameFromAttributes(attrs, usePreviewRules); ok {
return source.Source{Kind: source.HostnameKind, Identifier: host}, true
}

return source.Source{}, false
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/DataDog/datadog-agent/pkg/otlp/model/attributes/azure"
"github.com/DataDog/datadog-agent/pkg/otlp/model/internal/testutils"
"github.com/DataDog/datadog-agent/pkg/otlp/model/source"
)

const (
Expand All @@ -37,14 +38,14 @@ const (
testGCPIntegrationHostname = testHostName + "." + testCloudAccount
)

func TestHostnameFromAttributes(t *testing.T) {
func TestSourceFromAttributes(t *testing.T) {
tests := []struct {
name string
attrs pcommon.Map
usePreview bool

ok bool
hostname string
ok bool
src source.Source
}{
{
name: "custom hostname",
Expand All @@ -56,16 +57,16 @@ func TestHostnameFromAttributes(t *testing.T) {
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
}),
ok: true,
hostname: testCustomName,
ok: true,
src: source.Source{Kind: source.HostnameKind, Identifier: testCustomName},
},
{
name: "container ID",
attrs: testutils.NewAttributeMap(map[string]string{
conventions.AttributeContainerID: testContainerID,
}),
ok: true,
hostname: testContainerID,
ok: true,
src: source.Source{Kind: source.HostnameKind, Identifier: testContainerID},
},
{
name: "container ID, preview",
Expand All @@ -81,8 +82,8 @@ func TestHostnameFromAttributes(t *testing.T) {
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
}),
ok: true,
hostname: testHostName,
ok: true,
src: source.Source{Kind: source.HostnameKind, Identifier: testHostName},
},
{
name: "AWS EC2, preview",
Expand All @@ -92,7 +93,7 @@ func TestHostnameFromAttributes(t *testing.T) {
conventions.AttributeHostName: testHostName,
}),
ok: true,
hostname: testHostID,
src: source.Source{Kind: source.HostnameKind, Identifier: testHostID},
usePreview: true,
},
{
Expand All @@ -105,8 +106,8 @@ func TestHostnameFromAttributes(t *testing.T) {
conventions.AttributeAWSECSTaskRevision: "example-task-revision",
conventions.AttributeAWSECSLaunchtype: conventions.AttributeAWSECSLaunchtypeFargate,
}),
ok: true,
hostname: "",
ok: true,
src: source.Source{Kind: source.AWSECSFargateKind, Identifier: "example-task-ARN"},
},
{
name: "GCP",
Expand All @@ -115,8 +116,8 @@ func TestHostnameFromAttributes(t *testing.T) {
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testGCPHostname,
}),
ok: true,
hostname: testGCPHostname,
ok: true,
src: source.Source{Kind: source.HostnameKind, Identifier: testGCPHostname},
},
{
name: "GCP, preview",
Expand All @@ -128,7 +129,7 @@ func TestHostnameFromAttributes(t *testing.T) {
}),
usePreview: true,
ok: true,
hostname: testGCPIntegrationHostname,
src: source.Source{Kind: source.HostnameKind, Identifier: testGCPIntegrationHostname},
},
{
name: "azure",
Expand All @@ -137,8 +138,8 @@ func TestHostnameFromAttributes(t *testing.T) {
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
}),
ok: true,
hostname: testHostName,
ok: true,
src: source.Source{Kind: source.HostnameKind, Identifier: testHostName},
},
{
name: "azure, preview",
Expand All @@ -149,16 +150,16 @@ func TestHostnameFromAttributes(t *testing.T) {
}),
usePreview: true,
ok: true,
hostname: testHostID,
src: source.Source{Kind: source.HostnameKind, Identifier: testHostID},
},
{
name: "host id v. hostname",
attrs: testutils.NewAttributeMap(map[string]string{
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
}),
ok: true,
hostname: testHostID,
ok: true,
src: source.Source{Kind: source.HostnameKind, Identifier: testHostID},
},
{
name: "no hostname",
Expand All @@ -174,9 +175,9 @@ func TestHostnameFromAttributes(t *testing.T) {

for _, testInstance := range tests {
t.Run(testInstance.name, func(t *testing.T) {
hostname, ok := HostnameFromAttributes(testInstance.attrs, testInstance.usePreview)
source, ok := SourceFromAttributes(testInstance.attrs, testInstance.usePreview)
assert.Equal(t, testInstance.ok, ok)
assert.Equal(t, testInstance.hostname, hostname)
assert.Equal(t, testInstance.src, source)
})

}
Expand Down Expand Up @@ -224,7 +225,7 @@ func TestHostnameKubernetes(t *testing.T) {
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok := HostnameFromAttributes(attrs, false)
hostname, ok := hostnameFromAttributes(attrs, false)
assert.True(t, ok)
assert.Equal(t, hostname, "nodeName-clusterName")

Expand All @@ -235,18 +236,42 @@ func TestHostnameKubernetes(t *testing.T) {
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok = HostnameFromAttributes(attrs, false)
hostname, ok = hostnameFromAttributes(attrs, false)
assert.True(t, ok)
assert.Equal(t, hostname, "nodeName")

// Node name, no cluster name, AWS EC2, no preview
attrs = testutils.NewAttributeMap(map[string]string{
AttributeK8sNodeName: testNodeName,
conventions.AttributeContainerID: testContainerID,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS,
})
hostname, ok = hostnameFromAttributes(attrs, false)
assert.True(t, ok)
assert.Equal(t, hostname, "nodeName")

// Node name, no cluster name, AWS EC2, preview
attrs = testutils.NewAttributeMap(map[string]string{
AttributeK8sNodeName: testNodeName,
conventions.AttributeContainerID: testContainerID,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS,
})
hostname, ok = hostnameFromAttributes(attrs, true)
assert.True(t, ok)
assert.Equal(t, hostname, testHostID)

// no node name, cluster name
attrs = testutils.NewAttributeMap(map[string]string{
conventions.AttributeK8SClusterName: testClusterName,
conventions.AttributeContainerID: testContainerID,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok = HostnameFromAttributes(attrs, false)
hostname, ok = hostnameFromAttributes(attrs, false)
assert.True(t, ok)
// cluster name gets ignored, fallback to next option
assert.Equal(t, hostname, testHostID)
Expand Down
Loading