Skip to content

Commit

Permalink
[exporter/datadogexporter] Add Kubernetes conventions for hostnames (#…
Browse files Browse the repository at this point in the history
…1919)

* Add Kubernetes conventions for hostnames

* Address linting issues
  • Loading branch information
mx-psi authored Jan 3, 2021
1 parent 655db47 commit e9d7b1b
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 7 deletions.
29 changes: 26 additions & 3 deletions exporter/datadogexporter/metadata/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import (
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/utils/cache"
)

const (
AttributeDatadogHostname = "datadog.host.name"
AttributeK8sNodeName = "k8s.node.name"
)

// GetHost gets the hostname according to configuration.
// It checks in the following order
// 1. Cache
Expand Down Expand Up @@ -65,12 +70,28 @@ func GetHost(logger *zap.Logger, cfg *config.Config) *string {

// HostnameFromAttributes tries to get a valid hostname from attributes by checking, in order:
//
// 1. the container ID,
// 2. the cloud provider host ID and
// 3. the host.name attribute.
// 1. a custom Datadog hostname provided by the "datadog.host.name" attribute
// 2. the Kubernetes node name (and cluster name if available),
// 3. the container ID,
// 4. the cloud provider host ID and
// 5. the host.name attribute.
//
// It returns a boolean value indicated if any name was found
func HostnameFromAttributes(attrs pdata.AttributeMap) (string, bool) {
// Custom hostname: useful for overriding in k8s/cloud envs
if customHostname, ok := attrs.Get(AttributeDatadogHostname); ok {
return customHostname.StringVal(), true
}

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

// container id (e.g. from Docker)
if containerID, ok := attrs.Get(conventions.AttributeContainerID); ok {
return containerID.StringVal(), true
}
Expand All @@ -81,10 +102,12 @@ func HostnameFromAttributes(attrs pdata.AttributeMap) (string, bool) {
return ec2.HostnameFromAttributes(attrs)
}

// host id from cloud provider
if hostID, ok := attrs.Get(conventions.AttributeHostID); ok {
return hostID.StringVal(), true
}

// hostname from cloud provider or OS
if hostName, ok := attrs.Get(conventions.AttributeHostName); ok {
return hostName.StringVal(), true
}
Expand Down
73 changes: 69 additions & 4 deletions exporter/datadogexporter/metadata/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,46 @@ func TestHost(t *testing.T) {
assert.Equal(t, *host, osHostname)
}

const (
testHostID = "example-host-id"
testHostName = "example-host-name"
testContainerID = "example-container-id"
testClusterName = "clusterName"
testNodeName = "nodeName"
testCustomName = "example-custom-host-name"
)

func TestHostnameFromAttributes(t *testing.T) {
testHostID := "example-host-id"
testHostName := "example-host-name"
// Custom hostname
attrs := testutils.NewAttributeMap(map[string]string{
AttributeDatadogHostname: testCustomName,
AttributeK8sNodeName: testNodeName,
conventions.AttributeK8sCluster: testClusterName,
conventions.AttributeContainerID: testContainerID,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok := HostnameFromAttributes(attrs)
assert.True(t, ok)
assert.Equal(t, hostname, testCustomName)

// Container ID
attrs = testutils.NewAttributeMap(map[string]string{
conventions.AttributeContainerID: testContainerID,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok = HostnameFromAttributes(attrs)
assert.True(t, ok)
assert.Equal(t, hostname, testContainerID)

// AWS cloud provider means relying on the EC2 function
attrs := testutils.NewAttributeMap(map[string]string{
attrs = testutils.NewAttributeMap(map[string]string{
conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok := HostnameFromAttributes(attrs)
hostname, ok = HostnameFromAttributes(attrs)
assert.True(t, ok)
assert.Equal(t, hostname, testHostName)

Expand All @@ -76,5 +105,41 @@ func TestHostnameFromAttributes(t *testing.T) {
hostname, ok = HostnameFromAttributes(attrs)
assert.False(t, ok)
assert.Empty(t, hostname)
}

func TestHostnameKubernetes(t *testing.T) {
// Node name and cluster name
attrs := testutils.NewAttributeMap(map[string]string{
AttributeK8sNodeName: testNodeName,
conventions.AttributeK8sCluster: testClusterName,
conventions.AttributeContainerID: testContainerID,
conventions.AttributeHostID: testHostID,
conventions.AttributeHostName: testHostName,
})
hostname, ok := HostnameFromAttributes(attrs)
assert.True(t, ok)
assert.Equal(t, hostname, "nodeName-clusterName")

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

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

0 comments on commit e9d7b1b

Please sign in to comment.