pkg/sidecar/attributes.go (82 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package sidecar contains operations related to sidecar manipulation (Add, update, remove).
package sidecar
import (
"fmt"
"sort"
"strings"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"github.com/aws/amazon-cloudwatch-agent-operator/pkg/constants"
)
const resourceAttributesEnvName = "OTEL_RESOURCE_ATTRIBUTES"
type podReferences struct {
replicaset *appsv1.ReplicaSet
deployment *appsv1.Deployment
}
// getResourceAttributesEnv returns a list of environment variables. The list contains OTEL_RESOURCE_ATTRIBUTES and additional environment variables that use Kubernetes downward API to read pod specification.
// see: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/
func getResourceAttributesEnv(ns corev1.Namespace, podReferences podReferences) []corev1.EnvVar {
var envvars []corev1.EnvVar
attributes := map[attribute.Key]string{
semconv.K8SPodNameKey: fmt.Sprintf("$(%s)", constants.EnvPodName),
semconv.K8SPodUIDKey: fmt.Sprintf("$(%s)", constants.EnvPodUID),
semconv.K8SNodeNameKey: fmt.Sprintf("$(%s)", constants.EnvNodeName),
semconv.K8SNamespaceNameKey: ns.Name,
}
if podReferences.deployment != nil {
attributes[semconv.K8SDeploymentUIDKey] = string(podReferences.deployment.UID)
attributes[semconv.K8SDeploymentNameKey] = string(podReferences.deployment.Name)
}
if podReferences.replicaset != nil {
attributes[semconv.K8SReplicaSetUIDKey] = string(podReferences.replicaset.UID)
attributes[semconv.K8SReplicaSetNameKey] = string(podReferences.replicaset.Name)
}
envvars = append(envvars, corev1.EnvVar{
Name: constants.EnvPodName,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "metadata.name",
},
},
})
envvars = append(envvars, corev1.EnvVar{
Name: constants.EnvPodUID,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "metadata.uid",
},
},
})
envvars = append(envvars, corev1.EnvVar{
Name: constants.EnvNodeName,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "spec.nodeName",
},
},
})
envvars = append(envvars, corev1.EnvVar{
Name: resourceAttributesEnvName,
Value: mapToValue(attributes),
})
return envvars
}
func mapToValue(attributesMap map[attribute.Key]string) string {
var parts []string
// Sort it to make it predictable
keys := make([]string, 0, len(attributesMap))
for k := range attributesMap {
keys = append(keys, string(k))
}
sort.Strings(keys)
for _, key := range keys {
parts = append(parts, fmt.Sprintf("%s=%s", key, attributesMap[attribute.Key(key)]))
}
return strings.Join(parts, ",")
}
// check if container doesn't have already the OTEL_RESOURCE_ATTRIBUTES, we don't want to override it if it's already specified.
func hasResourceAttributeEnvVar(envvars []corev1.EnvVar) bool {
for _, env := range envvars {
if env.Name == resourceAttributesEnvName {
return true
}
}
return false
}