pkg/instrumentation/defaultinstrumentation.go (225 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package instrumentation
import (
"errors"
"fmt"
"os"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/aws/amazon-cloudwatch-agent-operator/apis/v1alpha1"
"github.com/aws/amazon-cloudwatch-agent-operator/internal/manifests/collector/adapters"
"github.com/aws/amazon-cloudwatch-agent-operator/pkg/instrumentation/jmx"
)
const (
defaultAPIVersion = "cloudwatch.aws.amazon.com/v1alpha1"
defaultInstrumentation = "java-instrumentation"
defaultNamespace = "default"
defaultKind = "Instrumentation"
http = "http"
https = "https"
java = "JAVA"
python = "PYTHON"
dotNet = "DOTNET"
nodeJS = "NODEJS"
limit = "LIMIT"
request = "REQUEST"
)
func getInstrumentationConfigForResource(langStr string, resourceStr string) corev1.ResourceList {
instrumentationConfigCpu, _ := os.LookupEnv("AUTO_INSTRUMENTATION_" + langStr + "_CPU_" + resourceStr)
instrumentationConfigMemory, _ := os.LookupEnv("AUTO_INSTRUMENTATION_" + langStr + "_MEM_" + resourceStr)
instrumentationConfigForResource := corev1.ResourceList{}
instrumentationConfigCpuQuantity, err := resource.ParseQuantity(instrumentationConfigCpu)
if err == nil {
instrumentationConfigForResource[corev1.ResourceCPU] = instrumentationConfigCpuQuantity
}
instrumentationConfigMemoryQuantity, err := resource.ParseQuantity(instrumentationConfigMemory)
if err == nil {
instrumentationConfigForResource[corev1.ResourceMemory] = instrumentationConfigMemoryQuantity
}
return instrumentationConfigForResource
}
func getDefaultInstrumentation(agentConfig *adapters.CwaConfig, additionalEnvs map[Type]map[string]string, isWindowsPod bool) (*v1alpha1.Instrumentation, error) {
javaInstrumentationImage, ok := os.LookupEnv("AUTO_INSTRUMENTATION_JAVA")
if !ok {
return nil, errors.New("unable to determine java instrumentation image")
}
pythonInstrumentationImage, ok := os.LookupEnv("AUTO_INSTRUMENTATION_PYTHON")
if !ok {
return nil, errors.New("unable to determine python instrumentation image")
}
dotNetInstrumentationImage, ok := os.LookupEnv("AUTO_INSTRUMENTATION_DOTNET")
if !ok {
return nil, errors.New("unable to determine dotnet instrumentation image")
}
nodeJSInstrumentationImage, ok := os.LookupEnv("AUTO_INSTRUMENTATION_NODEJS")
if !ok {
return nil, errors.New("unable to determine nodejs instrumentation image")
}
cloudwatchAgentServiceEndpoint := "cloudwatch-agent.amazon-cloudwatch"
if isWindowsPod {
// Windows pods use the headless service endpoint due to limitations with the agent on host network mode
// https://kubernetes.io/docs/concepts/services-networking/windows-networking/#limitations
cloudwatchAgentServiceEndpoint = "cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local"
}
// set protocol by checking cloudwatch agent config for tls setting
exporterPrefix := http
isApplicationSignalsEnabled := agentConfig != nil && agentConfig.GetApplicationSignalsMetricsConfig() != nil
if isApplicationSignalsEnabled {
if agentConfig.GetApplicationSignalsMetricsConfig().TLS != nil {
exporterPrefix = https
}
}
return &v1alpha1.Instrumentation{
Status: v1alpha1.InstrumentationStatus{},
TypeMeta: metav1.TypeMeta{
APIVersion: defaultAPIVersion,
Kind: defaultKind,
},
ObjectMeta: metav1.ObjectMeta{
Name: defaultInstrumentation,
Namespace: defaultNamespace,
},
Spec: v1alpha1.InstrumentationSpec{
Propagators: []v1alpha1.Propagator{
v1alpha1.TraceContext,
v1alpha1.Baggage,
v1alpha1.B3,
v1alpha1.XRay,
},
Java: v1alpha1.Java{
Image: javaInstrumentationImage,
Env: getJavaEnvs(isApplicationSignalsEnabled, cloudwatchAgentServiceEndpoint, exporterPrefix, additionalEnvs[TypeJava]),
Resources: corev1.ResourceRequirements{
Limits: getInstrumentationConfigForResource(java, limit),
Requests: getInstrumentationConfigForResource(java, request),
},
},
Python: v1alpha1.Python{
Image: pythonInstrumentationImage,
Env: getPythonEnvs(isApplicationSignalsEnabled, cloudwatchAgentServiceEndpoint, exporterPrefix, additionalEnvs[TypePython]),
Resources: corev1.ResourceRequirements{
Limits: getInstrumentationConfigForResource(python, limit),
Requests: getInstrumentationConfigForResource(python, request),
},
},
DotNet: v1alpha1.DotNet{
Image: dotNetInstrumentationImage,
Env: getDotNetEnvs(isApplicationSignalsEnabled, cloudwatchAgentServiceEndpoint, exporterPrefix, additionalEnvs[TypeDotNet]),
Resources: corev1.ResourceRequirements{
Limits: getInstrumentationConfigForResource(dotNet, limit),
Requests: getInstrumentationConfigForResource(dotNet, request),
},
},
NodeJS: v1alpha1.NodeJS{
Image: nodeJSInstrumentationImage,
Env: getNodeJSEnvs(isApplicationSignalsEnabled, cloudwatchAgentServiceEndpoint, exporterPrefix, additionalEnvs[TypeDotNet]),
Resources: corev1.ResourceRequirements{
Limits: getInstrumentationConfigForResource(nodeJS, limit),
Requests: getInstrumentationConfigForResource(nodeJS, request),
},
},
},
}, nil
}
func getJavaEnvs(isAppSignalsEnabled bool, cloudwatchAgentServiceEndpoint, exporterPrefix string, additionalEnvs map[string]string) []corev1.EnvVar {
envs := []corev1.EnvVar{
{Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"},
{Name: "OTEL_METRICS_EXPORTER", Value: "none"},
{Name: "OTEL_LOGS_EXPORTER", Value: "none"},
}
if isAppSignalsEnabled {
isJavaRuntimeEnabled, ok := os.LookupEnv("AUTO_INSTRUMENTATION_JAVA_RUNTIME_ENABLED")
if !ok {
isJavaRuntimeEnabled = "true"
}
appSignalsEnvs := []corev1.EnvVar{
{Name: "OTEL_AWS_APP_SIGNALS_ENABLED", Value: "true"}, //TODO: remove in favor of new name once safe
{Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"},
{Name: "OTEL_TRACES_SAMPLER_ARG", Value: fmt.Sprintf("endpoint=%s://%s:2000", http, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_TRACES_SAMPLER", Value: "xray"},
{Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/traces", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)}, //TODO: remove in favor of new name once safe
{Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", Value: isJavaRuntimeEnabled},
}
envs = append(envs, appSignalsEnvs...)
} else {
envs = append(envs, corev1.EnvVar{
Name: "OTEL_TRACES_EXPORTER", Value: "none",
})
}
var jmxEnvs []corev1.EnvVar
if targetSystems, ok := additionalEnvs[jmx.EnvTargetSystem]; ok {
jmxEnvs = []corev1.EnvVar{
{Name: "OTEL_AWS_JMX_EXPORTER_METRICS_ENDPOINT", Value: fmt.Sprintf("%s://%s:4314/v1/metrics", http, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_JMX_TARGET_SYSTEM", Value: targetSystems},
}
}
if len(jmxEnvs) != 0 {
envs = append(envs, jmxEnvs...)
}
return envs
}
func getPythonEnvs(isAppSignalsEnabled bool, cloudwatchAgentServiceEndpoint, exporterPrefix string, additionalEnvs map[string]string) []corev1.EnvVar {
var envs []corev1.EnvVar
if isAppSignalsEnabled {
isPythonRuntimeEnabled, ok := os.LookupEnv("AUTO_INSTRUMENTATION_PYTHON_RUNTIME_ENABLED")
if !ok {
isPythonRuntimeEnabled = "true"
}
envs = []corev1.EnvVar{
{Name: "OTEL_AWS_APP_SIGNALS_ENABLED", Value: "true"}, //TODO: remove in favor of new name once safe
{Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"},
{Name: "OTEL_TRACES_SAMPLER_ARG", Value: fmt.Sprintf("endpoint=%s://%s:2000", http, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_TRACES_SAMPLER", Value: "xray"},
{Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"},
{Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/traces", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_AWS_APP_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)}, //TODO: remove in favor of new name once safe
{Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", Value: isPythonRuntimeEnabled},
{Name: "OTEL_METRICS_EXPORTER", Value: "none"},
{Name: "OTEL_PYTHON_DISTRO", Value: "aws_distro"},
{Name: "OTEL_PYTHON_CONFIGURATOR", Value: "aws_configurator"},
{Name: "OTEL_LOGS_EXPORTER", Value: "none"},
}
}
return envs
}
func getDotNetEnvs(isAppSignalsEnabled bool, cloudwatchAgentServiceEndpoint, exporterPrefix string, additionalEnvs map[string]string) []corev1.EnvVar {
var envs []corev1.EnvVar
if isAppSignalsEnabled {
isDotNetRuntimeEnabled, ok := os.LookupEnv("AUTO_INSTRUMENTATION_DOTNET_RUNTIME_ENABLED")
if !ok {
isDotNetRuntimeEnabled = "true"
}
envs = []corev1.EnvVar{
{Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"},
{Name: "OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", Value: isDotNetRuntimeEnabled},
{Name: "OTEL_TRACES_SAMPLER_ARG", Value: fmt.Sprintf("endpoint=%s://%s:2000", http, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_TRACES_SAMPLER", Value: "xray"},
{Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"},
{Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/traces", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_METRICS_EXPORTER", Value: "none"},
{Name: "OTEL_DOTNET_DISTRO", Value: "aws_distro"},
{Name: "OTEL_DOTNET_CONFIGURATOR", Value: "aws_configurator"},
{Name: "OTEL_LOGS_EXPORTER", Value: "none"},
{Name: "OTEL_DOTNET_AUTO_PLUGINS", Value: "AWS.Distro.OpenTelemetry.AutoInstrumentation.Plugin, AWS.Distro.OpenTelemetry.AutoInstrumentation"},
}
}
return envs
}
func getNodeJSEnvs(isAppSignalsEnabled bool, cloudwatchAgentServiceEndpoint, exporterPrefix string, additionalEnvs map[string]string) []corev1.EnvVar {
var envs []corev1.EnvVar
if isAppSignalsEnabled {
envs = []corev1.EnvVar{
{Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"},
{Name: "OTEL_TRACES_SAMPLER_ARG", Value: fmt.Sprintf("endpoint=%s://%s:2000", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_TRACES_SAMPLER", Value: "xray"},
{Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"},
{Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/traces", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)},
{Name: "OTEL_METRICS_EXPORTER", Value: "none"},
{Name: "OTEL_LOGS_EXPORTER", Value: "none"},
}
}
return envs
}