// Copyright 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 attributes import ( "go.opentelemetry.io/collector/pdata/pcommon" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/azure" "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/ec2" "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/gcp" "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/source" ) const ( // AttributeDatadogHostname the datadog host name attribute AttributeDatadogHostname = "datadog.host.name" // AttributeK8sNodeName the datadog k8s node name attribute AttributeK8sNodeName = "k8s.node.name" // Attribute host is a literal host tag. // We check for this to avoid double tagging. AttributeHost = "host" ) func getClusterName(attrs pcommon.Map) (string, bool) { if k8sClusterName, ok := attrs.Get(conventions.AttributeK8SClusterName); ok { return k8sClusterName.Str(), true } cloudProvider, ok := attrs.Get(conventions.AttributeCloudProvider) if ok && cloudProvider.Str() == conventions.AttributeCloudProviderAzure { return azure.ClusterNameFromAttributes(attrs) } else if ok && cloudProvider.Str() == conventions.AttributeCloudProviderAWS { return ec2.ClusterNameFromAttributes(attrs) } return "", false } // hostnameFromAttributes tries to get a valid hostname from attributes by checking, in order: // // 1. the "host" attribute to avoid double tagging if present. // // 2. a custom Datadog hostname provided by the "datadog.host.name" attribute // // 3. cloud provider specific hostname for AWS, Azure or GCP, // // 4. the Kubernetes node name (and cluster name if available), // // 5. the cloud provider host ID and // // 6. the host.name attribute. // // It returns a boolean value indicated if any name was found func hostnameFromAttributes(attrs pcommon.Map) (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. var invalidHosts = map[string]struct{}{ "0.0.0.0": {}, "127.0.0.1": {}, "localhost": {}, "localhost.localdomain": {}, "localhost6.localdomain6": {}, "ip6-localhost": {}, } candidateHost, ok := unsanitizedHostnameFromAttributes(attrs) if _, invalid := invalidHosts[candidateHost]; invalid { return "", false } return candidateHost, ok } func k8sHostnameFromAttributes(attrs pcommon.Map) (string, bool) { node, ok := attrs.Get(AttributeK8sNodeName) if !ok { return "", false } if cluster, ok := getClusterName(attrs); ok { return node.Str() + "-" + cluster, true } return node.Str(), true } func unsanitizedHostnameFromAttributes(attrs pcommon.Map) (string, bool) { // Literal 'host' tag. Check and use to avoid double tagging. if literalHost, ok := attrs.Get(AttributeHost); ok { // Use even if not a string, so that we avoid double tagging if // `resource_attributes_as_tags` is true and 'host' has a non-string value. return literalHost.AsString(), true } // Custom hostname: useful for overriding in k8s/cloud envs if customHostname, ok := attrs.Get(AttributeDatadogHostname); ok { return customHostname.Str(), true } if launchType, ok := attrs.Get(conventions.AttributeAWSECSLaunchtype); ok && launchType.Str() == conventions.AttributeAWSECSLaunchtypeFargate { // If on AWS ECS Fargate, we don't have a hostname return "", false } cloudProvider, ok := attrs.Get(conventions.AttributeCloudProvider) switch { case ok && cloudProvider.Str() == conventions.AttributeCloudProviderAWS: return ec2.HostnameFromAttrs(attrs) case ok && cloudProvider.Str() == conventions.AttributeCloudProviderGCP: return gcp.HostnameFromAttrs(attrs) case ok && cloudProvider.Str() == conventions.AttributeCloudProviderAzure: return azure.HostnameFromAttrs(attrs) } // Kubernetes: node-cluster if cluster name is available, else node k8sName, k8sOk := k8sHostnameFromAttributes(attrs) if k8sOk { return k8sName, true } // host id from cloud provider if hostID, ok := attrs.Get(conventions.AttributeHostID); ok { return hostID.Str(), true } // hostname from cloud provider or OS if hostName, ok := attrs.Get(conventions.AttributeHostName); ok { return hostName.Str(), true } return "", false } // SourceFromAttrs gets a telemetry signal source from its attributes. func SourceFromAttrs(attrs pcommon.Map) (source.Source, bool) { if launchType, ok := attrs.Get(conventions.AttributeAWSECSLaunchtype); ok && launchType.Str() == conventions.AttributeAWSECSLaunchtypeFargate { if taskARN, ok := attrs.Get(conventions.AttributeAWSECSTaskARN); ok { return source.Source{Kind: source.AWSECSFargateKind, Identifier: taskARN.Str()}, true } } if host, ok := hostnameFromAttributes(attrs); ok { return source.Source{Kind: source.HostnameKind, Identifier: host}, true } return source.Source{}, false }