From dde56e01fcd1e310c62a5a3c229616206b1d6c9f Mon Sep 17 00:00:00 2001 From: Hyunsoo Kim Date: Tue, 23 Jun 2026 17:53:27 +0000 Subject: [PATCH 1/5] feat(translator): add Azure VM + AKS platform detection Detect Azure VM (via Azure IMDS 169.254.169.254 /metadata/instance with the mandatory "Metadata: true" header) and AKS (via the RUN_IN_AKS env var set by the AKS Helm chart), then drive the generated OTEL config from the detected platform with no customer input: - New translator/util/azuredetector package: cached IMDS probe (bounded timeout, transient-error retry, redirect refusal, compute-body validation, proxy disabled for the link-local address) + RUN_IN_AKS env signal. - AWS-first mode resolution: Azure is only considered after every AWS signal (RUN_IN_AWS, IRSA, EC2, ECS) is exhausted; EKS keeps precedence over AKS. AKS nodes resolve to the AzureVM host mode. - resourcedetection emits [azure, env] for Azure VM and [aks, env] for AKS. - oidctoken extension wired on Azure VM (Provider=azure) to obtain a bearer token from Azure IMDS for telemetry egress. Adds unit tests (httptest mock IMDS + env stubs), context mode handling, and golden tocwconfig fixtures for Azure VM and AKS. --- cfg/envconfig/envconfig.go | 8 + go.mod | 86 +-- go.sum | 170 ++--- service/defaultcomponents/components.go | 2 + service/defaultcomponents/components_test.go | 1 + translator/config/mode.go | 9 + translator/context/context.go | 8 +- translator/context/context_test.go | 95 +++ .../host_metrics_aks_config.conf | 15 + .../host_metrics_aks_config.json | 12 + .../host_metrics_aks_config.yaml | 618 ++++++++++++++++++ .../host_metrics_azurevm_config.conf | 15 + .../host_metrics_azurevm_config.json | 12 + .../host_metrics_azurevm_config.yaml | 455 +++++++++++++ translator/tocwconfig/tocwconfig_test.go | 19 + .../otel/extension/oidctoken/translator.go | 52 ++ .../extension/oidctoken/translator_test.go | 42 ++ .../resourcedetection/configs/aks_config.yaml | 3 + .../configs/azure_config.yaml | 3 + .../processor/resourcedetection/translator.go | 10 + .../resourcedetection/translator_test.go | 55 +- translator/translate/otel/translate_otel.go | 7 + .../util/azuredetector/azuredetector.go | 84 +++ .../util/azuredetector/azuredetector_test.go | 152 +++++ translator/util/sdkutil.go | 43 +- translator/util/sdkutil_test.go | 48 +- 26 files changed, 1879 insertions(+), 145 deletions(-) create mode 100644 translator/context/context_test.go create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.conf create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.conf create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml create mode 100644 translator/translate/otel/extension/oidctoken/translator.go create mode 100644 translator/translate/otel/extension/oidctoken/translator_test.go create mode 100644 translator/translate/otel/processor/resourcedetection/configs/aks_config.yaml create mode 100644 translator/translate/otel/processor/resourcedetection/configs/azure_config.yaml create mode 100644 translator/util/azuredetector/azuredetector.go create mode 100644 translator/util/azuredetector/azuredetector_test.go diff --git a/cfg/envconfig/envconfig.go b/cfg/envconfig/envconfig.go index 2677bdae12d..6883226a3fa 100644 --- a/cfg/envconfig/envconfig.go +++ b/cfg/envconfig/envconfig.go @@ -26,6 +26,7 @@ const ( RunInContainer = "RUN_IN_CONTAINER" RunAsHostProcessContainer = "RUN_AS_HOST_PROCESS_CONTAINER" RunInAWS = "RUN_IN_AWS" + RunInAKS = "RUN_IN_AKS" RunWithIRSA = "RUN_WITH_IRSA" RunWithSELinux = "RUN_WITH_SELINUX" RunInROSA = "RUN_IN_ROSA" @@ -89,6 +90,13 @@ func IsRunningInROSA() bool { return os.Getenv(RunInROSA) == TrueValue } +// IsRunningInAKS returns true when the RUN_IN_AKS environment variable is set to +// the expected true value. The CWA Helm chart for Azure Kubernetes Service (AKS) +// sets this variable so the translator can detect AKS without a metadata probe. +func IsRunningInAKS() bool { + return os.Getenv(RunInAKS) == TrueValue +} + func GetLogsBackpressureMode() string { return os.Getenv(CWAgentLogsBackpressureMode) } diff --git a/go.mod b/go.mod index 850a246203a..88721719613 100644 --- a/go.mod +++ b/go.mod @@ -9,66 +9,67 @@ replace collectd.org v0.4.0 => github.com/collectd/go-collectd v0.4.0 // Replace with https://github.com/amazon-contributing/opentelemetry-collector-contrib, there are no requirements for all receivers/processors/exporters // to be all replaced since there are some changes that will always be from upstream replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector => github.com/amazon-contributing/opentelemetry-collector-contrib/connector/routingconnector v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsemfexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260617204523-70530ca095f9 + github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector => github.com/amazon-contributing/opentelemetry-collector-contrib/connector/routingconnector v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsemfexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260629153525-3b0a252e5908 ) replace ( - github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/extension/awsproxy => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/headerssetterextension v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/sigv4authextension v0.0.0-20260617204523-70530ca095f9 + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/awsproxy => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/headerssetterextension v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidctokenextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/oidctokenextension v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/sigv4authextension v0.0.0-20260629153525-3b0a252e5908 ) replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutilv2 => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutilv2 v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/containerinsight => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/k8s => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/metrics => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/proxy => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/kubelet => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/sqlquery => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/sqlquery v0.0.0-20260617204523-70530ca095f9 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutilv2 => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutilv2 v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/containerinsight => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/k8s => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/metrics => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/proxy => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/kubelet => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/sqlquery => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/sqlquery v0.0.0-20260629153525-3b0a252e5908 ) replace ( // For clear resource attributes after copy functionality https://github.com/amazon-contributing/opentelemetry-collector-contrib/pull/148 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260617204523-70530ca095f9 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260629153525-3b0a252e5908 // Replace with contrib to revert upstream change https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/20519 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260617204523-70530ca095f9 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260629153525-3b0a252e5908 ) replace ( - github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws => github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260617204523-70530ca095f9 - github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 => github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 v0.0.0-20260617204523-70530ca095f9 + github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws => github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260629153525-3b0a252e5908 + github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 => github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 v0.0.0-20260629153525-3b0a252e5908 ) replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributestocontextprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/attributestocontextprocessor v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/awsattributelimitprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsattributelimitprocessor v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260617204523-70530ca095f9 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributestocontextprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/attributestocontextprocessor v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/awsattributelimitprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsattributelimitprocessor v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260629153525-3b0a252e5908 ) replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsefareceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsefareceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/postgresqlreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/postgresqlreceiver v0.0.0-20260617204523-70530ca095f9 - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260617204523-70530ca095f9 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsefareceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsefareceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/postgresqlreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/postgresqlreceiver v0.0.0-20260629153525-3b0a252e5908 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260629153525-3b0a252e5908 ) // Temporary fix, pending PR https://github.com/shirou/gopsutil/pull/957 @@ -139,7 +140,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/kardianos/service v1.2.1 // Keep this pinned to v1.2.1. v1.2.2 causes the agent to not register as a service on Windows github.com/knadh/koanf v1.5.0 - github.com/knadh/koanf/v2 v2.3.2 + github.com/knadh/koanf/v2 v2.3.4 github.com/kr/pretty v0.3.1 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c github.com/oklog/run v1.1.0 @@ -156,6 +157,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/ecsobserver v0.124.1 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidctokenextension v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.124.1 @@ -468,7 +470,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/go-version v1.9.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/nomad/api v0.0.0-20241218080744-e3ac00f30eec // indirect github.com/hashicorp/serf v0.10.1 // indirect diff --git a/go.sum b/go.sum index fc79dc53f64..2a9dd03657c 100644 --- a/go.sum +++ b/go.sum @@ -202,86 +202,88 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1483 h1:J8HaD+Zpfi1gcel3HCKpoHHEsrcuRrZlSnx7R9SCf5I= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1483/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= -github.com/amazon-contributing/opentelemetry-collector-contrib/connector/routingconnector v0.0.0-20260617204523-70530ca095f9 h1:BmqAg9/80IM4kWSv/sRNMOJe60MGpbT6YDiv0lnZTg8= -github.com/amazon-contributing/opentelemetry-collector-contrib/connector/routingconnector v0.0.0-20260617204523-70530ca095f9/go.mod h1:27Lstfad+LlHk17mhMuZh0vCAZcZxo3iwnE19aY/n7I= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260617204523-70530ca095f9 h1:ufqXnld+yUQilcSWi7I76biYBt1bz1sxCEoG3WiVMOI= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260617204523-70530ca095f9/go.mod h1:7IC5iRiEVjkl3gQkyGF54WJrvAPNwRPp88JmYlPszu8= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260617204523-70530ca095f9 h1:tQIvwArUrAr3KJ+ptrdtYhoJVqQdndgIo/yS3pqrbXs= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260617204523-70530ca095f9/go.mod h1:3CyMeKLU/pTjhm0qL4WEbYtq4/IaHJsOF8e7WiweXJQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260617204523-70530ca095f9 h1:uMWxXJSbWwg0AOIfCsxV13xNp91+u0CyxZCvI+hPeRc= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260617204523-70530ca095f9/go.mod h1:Jm0k76CRZ1uR0B6kFHYlLPe7AxWJu69aIrCQiygTBFg= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension v0.0.0-20260617204523-70530ca095f9 h1:l0fkhH1tfvEFez45FEbimOP2mH6oGo82qJ6h8kYdZrA= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension v0.0.0-20260617204523-70530ca095f9/go.mod h1:9MisC4YKCPC4y2iYH4qqnrEILf25HSSaPRQMuUpcgjA= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260617204523-70530ca095f9 h1:9WlmMVSuzLVH36YuQx5zEJQ2+VFHoyekfH6PAB0uvv0= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260617204523-70530ca095f9/go.mod h1:KQqi2/SDM6SveSbJmH7yE2ZJrcwvQEsq8fGgj7J42oI= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260617204523-70530ca095f9 h1:KM3MCxYh5wszurH+cCsxgnRoDNWoSJUtrVavmzPghH8= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260617204523-70530ca095f9/go.mod h1:tAN1C96iJCDwxl9qMnno54d6sQwxHAVSYdpDJjc6bbY= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/headerssetterextension v0.0.0-20260617204523-70530ca095f9 h1:XDdiplV9Vm/ajOQrglZC8SwtDB7ZUuLHXrkP7S6q8/w= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/headerssetterextension v0.0.0-20260617204523-70530ca095f9/go.mod h1:2JFFL30N2NI5N8LbjcLWXYTsoDMDYbwItyZ9ntp8++E= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/sigv4authextension v0.0.0-20260617204523-70530ca095f9 h1:zC597TKjmPODTzlSA1Vf6hUl+oQaaZ836JypPr1dDrU= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/sigv4authextension v0.0.0-20260617204523-70530ca095f9/go.mod h1:T+KX7lUiH1rbWkTq1ZB2ku94qf1qI70SbA0r5ZW/+XA= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260617204523-70530ca095f9 h1:v2tkQyJyjeQrOxUUgudYjSedZ2sHhqt0wIVj3LgmsfQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260617204523-70530ca095f9/go.mod h1:RkX+mCTIbF0RTiqXFe0Wo6a1w0cULc23osRU5IRmqVQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutilv2 v0.0.0-20260617204523-70530ca095f9 h1:hETuHCryDPmcEYGneGPoArX1M9ToJJXgJecxq/x1uyo= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutilv2 v0.0.0-20260617204523-70530ca095f9/go.mod h1:eu7CU/WFl6msRQ6TKTkEljdUPVhs47LoY+IB8KOXVaE= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260617204523-70530ca095f9 h1:P6rAUvAsnvczZRD2EBRay68F2Ccc0Wo2KIzGG03mwfs= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260617204523-70530ca095f9/go.mod h1:DwMzm9PGyRRVkekICZdCOHt/kf6A/z9YQ7G2DqspJ4g= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260617204523-70530ca095f9 h1:kijsTXJqm5a0fFEwEjHVuNu10vyDzZJKWVCszXSjK8E= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260617204523-70530ca095f9/go.mod h1:TFjXft/gNwEk5EIrNz3glFCx6mMbJNgORAnpKunDw4Y= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260617204523-70530ca095f9 h1:0mbgsvsDP43ZW347bKTcjS0cnPIdGpeynFdCIvDXWTA= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260617204523-70530ca095f9/go.mod h1:g0K7XRwXDigDe6QLvoUwCFglvzBZYepLDq3JGcYc8t0= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260617204523-70530ca095f9 h1:TEJfVXHm+0pnp8NDWP6t1AF4vclBx1UWd5fTwsjSN3Q= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260617204523-70530ca095f9/go.mod h1:AYAFBvDLMHLC5jJCIfUtfmLAzUdwlkBk8IyZ42ZKAco= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260617204523-70530ca095f9 h1:ood1l4+hxWgziSaRMFMGiXtJKW7nnoLHMexC+HywEwc= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260617204523-70530ca095f9/go.mod h1:CYuz358AgDWlVFiv0YUtttDPwz4F01dXLLmyFlGBcSI= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260617204523-70530ca095f9 h1:LWOZs60MwGDdFHvOf9CPQOK78ZqgCCTfk2f2+CW4wsA= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260617204523-70530ca095f9/go.mod h1:zK0mPhzOwJJBAIV0reGIlm3UB0QlOrEqZYRTYGB2vt4= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260617204523-70530ca095f9 h1:mThkV1q4AcMpnOh/CoBPai8TpUVL04bWV4nhaPnDsuY= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260617204523-70530ca095f9/go.mod h1:6IoOrz1DvWaBT8+aGFZ1UznDfXKu35/ifj/sk28EQbQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260617204523-70530ca095f9 h1:Ys4v53PQ8IyxCMbexDanTJKhvom2xSgoyZMfiyKS7vI= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260617204523-70530ca095f9/go.mod h1:ie5XUZZf62AI/Is0cBsCNnab4tLow1FCQZlvJPei2EU= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260617204523-70530ca095f9 h1:0EAwF4Xd2b9m926uQbzn5+lphcd5O8jLIGyM3NEjh2U= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260617204523-70530ca095f9/go.mod h1:N2GwwIf7QhML2Lcdh3UCMxWCK4uViTUa+gx2as1sfo8= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260617204523-70530ca095f9 h1:rIOZJqt1Nn6eiJaAiJhtAnkqAGCnTOZWui7lhwF5lz8= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260617204523-70530ca095f9/go.mod h1:YnKeE9wRjdoReCSbDK5JRqVmusZNRyG/dG2R04WbWCQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/sqlquery v0.0.0-20260617204523-70530ca095f9 h1:urtBXQQwDY4cjIWULyDqHQ1H5TvD4YaqVsFj/mm6vCI= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/sqlquery v0.0.0-20260617204523-70530ca095f9/go.mod h1:ONQIjCIQvfNkzG3d3tHa8Unen0sx1HXv/T3t+J6Dg30= -github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260617204523-70530ca095f9 h1:E6zIfMbsPK4zAGiHX72dmsfc/aERjpOkdZRP0+Gd8BY= -github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260617204523-70530ca095f9/go.mod h1:jGegGUX4PViU3gFU/dPvLUXuXC8GRiY9EYRpJhlczZA= -github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 v0.0.0-20260617204523-70530ca095f9 h1:30+3TVd4HahKPlNeCSr2NckA/GPoEFVUfSQjtzY6XTo= -github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 v0.0.0-20260617204523-70530ca095f9/go.mod h1:OJDM0SubQwycFghb9NhWv6DqlhmMUxbToFVxtCTPku8= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260617204523-70530ca095f9 h1:Hw+3czZCSsYP2LmM4Ygd1RRXgDyQ3Y5bLePvM7bTwfM= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260617204523-70530ca095f9/go.mod h1:xrcHPb5eq5BblbNj64IpYqv295gwzpSUPgih7Dyo65E= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260617204523-70530ca095f9 h1:qVcxDdUoxuojYz0aKfcqcph68uJ6e4vg09vw6Ny31/g= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260617204523-70530ca095f9/go.mod h1:2SjHFWsA3UKr7N4IOP5ph0inaZtn6bteD9DitRAASEc= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260617204523-70530ca095f9 h1:NQZNvDzn45uuXEvh0dUL4Qn4bUKGo7P+mR6Bbah9k5g= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260617204523-70530ca095f9/go.mod h1:mfl6tGXnVyT0+nga+Oml7bMNWKempeGTtL+5Vs2LzHA= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/attributestocontextprocessor v0.0.0-20260617204523-70530ca095f9 h1:WyJu+VMGbDGhVFX4kBc8Gi145vFKQyjZ95iv7OVHCkY= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/attributestocontextprocessor v0.0.0-20260617204523-70530ca095f9/go.mod h1:LkMDqpRrHT2MNeobnhdNZbY8JrDVFbL08dr/Ayd3JIk= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsattributelimitprocessor v0.0.0-20260617204523-70530ca095f9 h1:jrF9190gXrAsXegt+yFBR917PiYwqmbINuKLse0ybgc= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsattributelimitprocessor v0.0.0-20260617204523-70530ca095f9/go.mod h1:OFDM1K0I68jJO8s5S0+5xDwnqscnRnYmYgZxA8bbhic= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor v0.0.0-20260617204523-70530ca095f9 h1:K3ORYKE6xlRBRTb8YqxaRnsyHw2E1XfDX4e82UjaakA= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor v0.0.0-20260617204523-70530ca095f9/go.mod h1:Gf1ZUZY1Lszko2Uq/Oc25Wl1gSwXNa2tGiJM66/H7Gc= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260617204523-70530ca095f9 h1:CugY/0kXZlkH4OPhY4bG/OI0uLleWQ09CqpuVaSQkrE= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260617204523-70530ca095f9/go.mod h1:MhLjAHeQxgPyj1yyEAqTFfcU/U9tp4yLWYUV4f+xwYw= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260617204523-70530ca095f9 h1:Lxgg4P1eRHaNdol4DwZOp9o0Nq4jCx9CgZysLc94d+M= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260617204523-70530ca095f9/go.mod h1:BsyajnM+KIXy6wlP7NCfWf8tEFAIIG4RmK2zbb3k8Vo= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260617204523-70530ca095f9 h1:6oOPCF1jYi/h2dXzoOPdCRHyVdAai83noscWzsOzMFY= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:zFCrWSeArOUe0UgX0XUli3ECGUOZ9BKD7AM+kpbaIv8= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260617204523-70530ca095f9 h1:ufPbWsmxk6lZYw0VlBzwcwUCAAQnmvXmNY2HXnSr6nk= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:7FA4owN95eAy2RD/7IOC5d1be2P0DyZT7nOQZruwtls= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsefareceiver v0.0.0-20260617204523-70530ca095f9 h1:0Le06UclFhOpvUHrEzxIuark3fhXvM52RrRuXcX1BC4= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsefareceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:P04h4k7pXBfr/ZzKZTf3lA389OtLMOV4VVv5lYEsHng= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver v0.0.0-20260617204523-70530ca095f9 h1:mJGGom1uSTyvXvxNZR7GOyaeLI0hfIlRvT4ArwPd4C8= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:O5EpYcUzu+RgVImSc2DdwZ5EztdHPmwpHfuaA2WLh1Y= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260617204523-70530ca095f9 h1:6sN6qnEJHVeDo72pZg8U3TWmFm3zROzPCuKWAYUtMzI= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:gNDpUc9eYHKy9YqGYK8toPIo3QZKdYQFLnaVQDA32A0= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260617204523-70530ca095f9 h1:R4Ems+YMAXLtsEZwRXw8/cf1NkrzHQsc25WPdYbUaTM= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:wQV77sHJJdUMIvbJna8LjlNb0omEdiVSrwQnoVAlTMM= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/postgresqlreceiver v0.0.0-20260617204523-70530ca095f9 h1:ZLP3mZHMXzUjiXxXFVI9Z5LtU25qT13Qajz84mna/LY= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/postgresqlreceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:gSxi2fY+T8mNzlaU7qOEnaXTcKfhIpijDSyeCu5GOYo= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260617204523-70530ca095f9 h1:p0HB9BwhUdkaI0ALIb3hYxi7oCTDsGUuGadeM3R36AM= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260617204523-70530ca095f9/go.mod h1:pa2WwKwnYyS/Wviu8P/uCm7JmtrxtB+IG1f8+b+VJF0= +github.com/amazon-contributing/opentelemetry-collector-contrib/connector/routingconnector v0.0.0-20260629153525-3b0a252e5908 h1:lkb5AAxuruGmsjyUo0w5PXtqu8DRyOxu3Ws1GHfj7gg= +github.com/amazon-contributing/opentelemetry-collector-contrib/connector/routingconnector v0.0.0-20260629153525-3b0a252e5908/go.mod h1:27Lstfad+LlHk17mhMuZh0vCAZcZxo3iwnE19aY/n7I= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260629153525-3b0a252e5908 h1:fQy+39IkDqj8GGUJfcrTBLRfLK3GepJ66TqIEn48d0U= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260629153525-3b0a252e5908/go.mod h1:7IC5iRiEVjkl3gQkyGF54WJrvAPNwRPp88JmYlPszu8= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260629153525-3b0a252e5908 h1:Rs6GR6I5VZMM/A6s4okmoHbpIqQNX1RJZAAWDQSIXbA= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260629153525-3b0a252e5908/go.mod h1:3CyMeKLU/pTjhm0qL4WEbYtq4/IaHJsOF8e7WiweXJQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260629153525-3b0a252e5908 h1:6VrFHTmZOtNNqKP9340q/jFEEBzdJXGsyE0kWi4cThs= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260629153525-3b0a252e5908/go.mod h1:Jm0k76CRZ1uR0B6kFHYlLPe7AxWJu69aIrCQiygTBFg= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension v0.0.0-20260629153525-3b0a252e5908 h1:6bqd8nxF4Md+PQ7ViCaym603Y70ylqAUmuoqHB8Vl38= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awscloudwatchlogsprovisionerextension v0.0.0-20260629153525-3b0a252e5908/go.mod h1:9MisC4YKCPC4y2iYH4qqnrEILf25HSSaPRQMuUpcgjA= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260629153525-3b0a252e5908 h1:2GmHLxknJ32Ie6zS1dJHITYUT3WWm6dyNKFrzwy6cPs= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260629153525-3b0a252e5908/go.mod h1:KQqi2/SDM6SveSbJmH7yE2ZJrcwvQEsq8fGgj7J42oI= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260629153525-3b0a252e5908 h1:1lyGj8eF6of9duz2k42bNNh2uBL1GAfVXYvpx43uZiQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260629153525-3b0a252e5908/go.mod h1:tAN1C96iJCDwxl9qMnno54d6sQwxHAVSYdpDJjc6bbY= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/headerssetterextension v0.0.0-20260629153525-3b0a252e5908 h1:zHnjS+U7w9ePE9viBFyfRS14o0+NCMF9S6qudYkhPBI= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/headerssetterextension v0.0.0-20260629153525-3b0a252e5908/go.mod h1:2JFFL30N2NI5N8LbjcLWXYTsoDMDYbwItyZ9ntp8++E= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/oidctokenextension v0.0.0-20260629153525-3b0a252e5908 h1:bCp1HeIDvvv+cFFx+21kjF8yzAyQZRNW6Gnu+dN8fzA= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/oidctokenextension v0.0.0-20260629153525-3b0a252e5908/go.mod h1:TE6yrCGcu1zdBdOa/8yMLWSNr8cMGGFht6S/bml2FuY= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/sigv4authextension v0.0.0-20260629153525-3b0a252e5908 h1:KtTC/8ySv+pH9IBO0PkwWXcgshAqaZL5TbaKNCrueFU= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/sigv4authextension v0.0.0-20260629153525-3b0a252e5908/go.mod h1:T+KX7lUiH1rbWkTq1ZB2ku94qf1qI70SbA0r5ZW/+XA= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260629153525-3b0a252e5908 h1:xrtYAF8betlpuL3C4Kp6lGu0kkzBShfKrSd64D5AACQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260629153525-3b0a252e5908/go.mod h1:RkX+mCTIbF0RTiqXFe0Wo6a1w0cULc23osRU5IRmqVQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutilv2 v0.0.0-20260629153525-3b0a252e5908 h1:M759RK6Sz8p4s3yd2rjyk2SmJIuCtYDUSTboeNi8Rno= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutilv2 v0.0.0-20260629153525-3b0a252e5908/go.mod h1:eu7CU/WFl6msRQ6TKTkEljdUPVhs47LoY+IB8KOXVaE= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260629153525-3b0a252e5908 h1:j0tnXUmxhHHxpxo/EH4mP1uT+dUUE5Xk1Htp6zYy9gA= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260629153525-3b0a252e5908/go.mod h1:DwMzm9PGyRRVkekICZdCOHt/kf6A/z9YQ7G2DqspJ4g= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260629153525-3b0a252e5908 h1:o0z6OwK/4ZwL5tg1OGvBcyYxLn+miMt2Cxh9xoR4VAw= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260629153525-3b0a252e5908/go.mod h1:TFjXft/gNwEk5EIrNz3glFCx6mMbJNgORAnpKunDw4Y= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260629153525-3b0a252e5908 h1:pt3RQjSs42TCGw06GrCZB7WtdfPsz3SInFBds1LZB2I= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260629153525-3b0a252e5908/go.mod h1:g0K7XRwXDigDe6QLvoUwCFglvzBZYepLDq3JGcYc8t0= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260629153525-3b0a252e5908 h1:xKDbrCXTH0XTPJteLhoWgdN9yvgJyBU4AOr57wvw+uA= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260629153525-3b0a252e5908/go.mod h1:AYAFBvDLMHLC5jJCIfUtfmLAzUdwlkBk8IyZ42ZKAco= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260629153525-3b0a252e5908 h1:1lntX57OdzK8ma4UR5Gfl38gTX+J5rMjqG0uOqiGo4k= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260629153525-3b0a252e5908/go.mod h1:CYuz358AgDWlVFiv0YUtttDPwz4F01dXLLmyFlGBcSI= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260629153525-3b0a252e5908 h1:bSeQ+D7dVHcy9LbYc2HI772TQZM7O28y0udPILWzQJQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260629153525-3b0a252e5908/go.mod h1:zK0mPhzOwJJBAIV0reGIlm3UB0QlOrEqZYRTYGB2vt4= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260629153525-3b0a252e5908 h1:fKByOgsC95F3ASkomQN6u7+XD8qS3xAvYhCwdQSpWKs= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260629153525-3b0a252e5908/go.mod h1:6IoOrz1DvWaBT8+aGFZ1UznDfXKu35/ifj/sk28EQbQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260629153525-3b0a252e5908 h1:k+UnYnvF1r1FjVBg93HfCBmQmDJh2yf8holB6U11nPs= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260629153525-3b0a252e5908/go.mod h1:ie5XUZZf62AI/Is0cBsCNnab4tLow1FCQZlvJPei2EU= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260629153525-3b0a252e5908 h1:TioOmiFaadWekjCoWng5mz+t7WxUmSDwesLviaYShYU= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260629153525-3b0a252e5908/go.mod h1:N2GwwIf7QhML2Lcdh3UCMxWCK4uViTUa+gx2as1sfo8= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260629153525-3b0a252e5908 h1:ppBCGt++1F9fDSY8NdEZzps6pS1xIQe6jRahEnQKXBY= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260629153525-3b0a252e5908/go.mod h1:YnKeE9wRjdoReCSbDK5JRqVmusZNRyG/dG2R04WbWCQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/sqlquery v0.0.0-20260629153525-3b0a252e5908 h1:GNm9xQITDG3DWtQpdLnkDjyIX8YkVqDzUenVequtebE= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/sqlquery v0.0.0-20260629153525-3b0a252e5908/go.mod h1:ONQIjCIQvfNkzG3d3tHa8Unen0sx1HXv/T3t+J6Dg30= +github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260629153525-3b0a252e5908 h1:m3i9Z4nUMwy2mUODr7CpStuSdKPfoZn/bDzthHZjEHQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260629153525-3b0a252e5908/go.mod h1:jGegGUX4PViU3gFU/dPvLUXuXC8GRiY9EYRpJhlczZA= +github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 v0.0.0-20260629153525-3b0a252e5908 h1:NxzsfJNt60uLx82meHROGregHIr7fIrXq5j714hq99I= +github.com/amazon-contributing/opentelemetry-collector-contrib/override/awsv2 v0.0.0-20260629153525-3b0a252e5908/go.mod h1:OJDM0SubQwycFghb9NhWv6DqlhmMUxbToFVxtCTPku8= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260629153525-3b0a252e5908 h1:I7ObUsREis+y8T+WHoxqntzCFVaU6otdPziepzRiX8M= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260629153525-3b0a252e5908/go.mod h1:xrcHPb5eq5BblbNj64IpYqv295gwzpSUPgih7Dyo65E= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260629153525-3b0a252e5908 h1:rYTKknn7ta4R8IoaVIDi0jgrZ+GEsIqIGiqREkyAv6U= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260629153525-3b0a252e5908/go.mod h1:2SjHFWsA3UKr7N4IOP5ph0inaZtn6bteD9DitRAASEc= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260629153525-3b0a252e5908 h1:hhXF2cCkacnn7iQCLPCWpGNdYa8U9xEy7gPqpFfP+aA= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260629153525-3b0a252e5908/go.mod h1:mfl6tGXnVyT0+nga+Oml7bMNWKempeGTtL+5Vs2LzHA= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/attributestocontextprocessor v0.0.0-20260629153525-3b0a252e5908 h1:945CVmvzn7KF78RbebNY3o2v9W+Si6vLUc//CM9kDU0= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/attributestocontextprocessor v0.0.0-20260629153525-3b0a252e5908/go.mod h1:LkMDqpRrHT2MNeobnhdNZbY8JrDVFbL08dr/Ayd3JIk= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsattributelimitprocessor v0.0.0-20260629153525-3b0a252e5908 h1:YtL4P58iZie6V6DgZN6eNNu/gra9QXDutvynzQsCrE4= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsattributelimitprocessor v0.0.0-20260629153525-3b0a252e5908/go.mod h1:OFDM1K0I68jJO8s5S0+5xDwnqscnRnYmYgZxA8bbhic= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor v0.0.0-20260629153525-3b0a252e5908 h1:QP1gKF1MrnexytSAWQZTHcFvu7BmEVSd09mrKlQjLHc= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/awsdevicepodcorrelationprocessor v0.0.0-20260629153525-3b0a252e5908/go.mod h1:Gf1ZUZY1Lszko2Uq/Oc25Wl1gSwXNa2tGiJM66/H7Gc= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260629153525-3b0a252e5908 h1:/L93QtbKhH5Gy5xLBDP5LWvsvbdO54P0JsETEaRH8UE= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260629153525-3b0a252e5908/go.mod h1:MhLjAHeQxgPyj1yyEAqTFfcU/U9tp4yLWYUV4f+xwYw= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260629153525-3b0a252e5908 h1:+3LWUJslxDCm35ks9CYrNmM0tv34MZm4RzAW6k3Woq8= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260629153525-3b0a252e5908/go.mod h1:BsyajnM+KIXy6wlP7NCfWf8tEFAIIG4RmK2zbb3k8Vo= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260629153525-3b0a252e5908 h1:rBx2JWhXlD4fYhjeXplG+OpVBbGHSCnaJBzxFCRqI3o= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:zFCrWSeArOUe0UgX0XUli3ECGUOZ9BKD7AM+kpbaIv8= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260629153525-3b0a252e5908 h1:AoJ8YhrBoWWVXhr2u+x/MMy5Aw9J5s6fipccxYZqDbg= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:7FA4owN95eAy2RD/7IOC5d1be2P0DyZT7nOQZruwtls= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsefareceiver v0.0.0-20260629153525-3b0a252e5908 h1:eyeKmaWfv9wLKTjzIWEZ/0vePK6sfX+9vSwGL1genTY= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsefareceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:P04h4k7pXBfr/ZzKZTf3lA389OtLMOV4VVv5lYEsHng= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver v0.0.0-20260629153525-3b0a252e5908 h1:oAt+b3fIHNAsEn4KI1UVQlDQzOfx2R+GCTDjz/uA1Pc= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsekshyperpodreceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:O5EpYcUzu+RgVImSc2DdwZ5EztdHPmwpHfuaA2WLh1Y= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260629153525-3b0a252e5908 h1:Gq/xFzqPiscZuYghGad8PFJm4po5FFDCgkR6RBXqgy4= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:gNDpUc9eYHKy9YqGYK8toPIo3QZKdYQFLnaVQDA32A0= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260629153525-3b0a252e5908 h1:weXQal9EqnmzhLqFYkOekx3xBtI70O3ZlhaN1SED0Ag= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:wQV77sHJJdUMIvbJna8LjlNb0omEdiVSrwQnoVAlTMM= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/postgresqlreceiver v0.0.0-20260629153525-3b0a252e5908 h1:VA9sQrbWV2AGqviF1uplR62N8YXpax/DFi04lyu7hqg= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/postgresqlreceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:gSxi2fY+T8mNzlaU7qOEnaXTcKfhIpijDSyeCu5GOYo= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260629153525-3b0a252e5908 h1:HovegqSrtha9jIg+G+ZWEhYX1dc4mGqHetoE6gOkqEw= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260629153525-3b0a252e5908/go.mod h1:pa2WwKwnYyS/Wviu8P/uCm7JmtrxtB+IG1f8+b+VJF0= github.com/amir/raidman v0.0.0-20170415203553-1ccc43bfb9c9 h1:FXrPTd8Rdlc94dKccl7KPmdmIbVh/OjelJ8/vgMRzcQ= github.com/amir/raidman v0.0.0-20170415203553-1ccc43bfb9c9/go.mod h1:eliMa/PW+RDr2QLWRmLH1R1ZA4RInpmvOzDDXtaIZkc= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -909,8 +911,8 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= -github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -1060,8 +1062,8 @@ github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kK github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= -github.com/knadh/koanf/v2 v2.3.2 h1:Ee6tuzQYFwcZXQpc2MiVeC6qHMandf5SMUJJNoFp/c4= -github.com/knadh/koanf/v2 v2.3.2/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= +github.com/knadh/koanf/v2 v2.3.4 h1:fnynNSDlujWE+v83hAp8wKr/cdoxHLO0629SN+U8Urc= +github.com/knadh/koanf/v2 v2.3.4/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= diff --git a/service/defaultcomponents/components.go b/service/defaultcomponents/components.go index 62818f1898d..7ba2346894d 100644 --- a/service/defaultcomponents/components.go +++ b/service/defaultcomponents/components.go @@ -17,6 +17,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension" "github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension" "github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/ecsobserver" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidctokenextension" "github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension" "github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension" "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage" @@ -187,6 +188,7 @@ func Factories() (otelcol.Factories, error) { ecsobserver.NewFactory(), filestorage.NewFactory(), healthcheckextension.NewFactory(), + oidctokenextension.NewFactory(), pprofextension.NewFactory(), sigv4authextension.NewFactory(), zpagesextension.NewFactory(), diff --git a/service/defaultcomponents/components_test.go b/service/defaultcomponents/components_test.go index 88137236ea6..1ea217dfa83 100644 --- a/service/defaultcomponents/components_test.go +++ b/service/defaultcomponents/components_test.go @@ -126,6 +126,7 @@ func TestComponents(t *testing.T) { "health_check", "k8smetadata", "nodemetadatacache", + "oidctoken", "pprof", "server", "sigv4auth", diff --git a/translator/config/mode.go b/translator/config/mode.go index 5f8c8f93ede..dca470e6b97 100644 --- a/translator/config/mode.go +++ b/translator/config/mode.go @@ -20,6 +20,13 @@ const ( ModeK8sOnPrem = "K8sOnPrem" ) +// Azure platform modes. ModeAzureVM is a host-level mode analogous to +// ModeEC2; ModeAKS is a Kubernetes-level mode analogous to ModeEKS. +const ( + ModeAzureVM = "AzureVM" + ModeAKS = "AKS" +) + const ( ShortModeEC2 = "EC2" ShortModeOnPrem = "OP" @@ -27,4 +34,6 @@ const ( ShortModeEKS = "EKS" ShortModeK8sEC2 = "K8E" ShortModeK8sOnPrem = "K8OP" + ShortModeAzureVM = "AZVM" + ShortModeAKS = "AKS" ) diff --git a/translator/context/context.go b/translator/context/context.go index 00b790b63b9..cf94ee679df 100644 --- a/translator/context/context.go +++ b/translator/context/context.go @@ -129,8 +129,11 @@ func (ctx *Context) SetMode(mode string) { case config.ModeWithIRSA: ctx.mode = config.ModeWithIRSA ctx.shortMode = config.ShortModeWithIRSA + case config.ModeAzureVM: + ctx.mode = config.ModeAzureVM + ctx.shortMode = config.ShortModeAzureVM default: - log.Panicf("Invalid mode %s. Valid mode values are %s, %s, %s, and %s.", mode, config.ModeEC2, config.ModeOnPrem, config.ModeOnPremise, config.ModeWithIRSA) + log.Panicf("Invalid mode %s. Valid mode values are %s, %s, %s, %s, and %s.", mode, config.ModeEC2, config.ModeOnPrem, config.ModeOnPremise, config.ModeWithIRSA, config.ModeAzureVM) } } @@ -145,6 +148,9 @@ func (ctx *Context) SetKubernetesMode(mode string) { case config.ModeK8sOnPrem: ctx.kubernetesMode = config.ModeK8sOnPrem ctx.shortMode = config.ShortModeK8sOnPrem + case config.ModeAKS: + ctx.kubernetesMode = config.ModeAKS + ctx.shortMode = config.ShortModeAKS default: ctx.kubernetesMode = "" } diff --git a/translator/context/context_test.go b/translator/context/context_test.go new file mode 100644 index 00000000000..50dd7b63b0b --- /dev/null +++ b/translator/context/context_test.go @@ -0,0 +1,95 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package context + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/aws/amazon-cloudwatch-agent/translator/config" +) + +func TestSetMode_Azure(t *testing.T) { + ResetContext() + ctx := CurrentContext() + + ctx.SetMode(config.ModeAzureVM) + assert.Equal(t, config.ModeAzureVM, ctx.Mode()) + assert.Equal(t, config.ShortModeAzureVM, ctx.ShortMode()) +} + +func TestSetMode_ExistingModesUnchanged(t *testing.T) { + cases := map[string]struct { + mode string + wantShort string + }{ + "EC2": {config.ModeEC2, config.ShortModeEC2}, + "OnPrem": {config.ModeOnPrem, config.ShortModeOnPrem}, + "WithIRSA": {config.ModeWithIRSA, config.ShortModeWithIRSA}, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + ResetContext() + ctx := CurrentContext() + ctx.SetMode(tc.mode) + assert.Equal(t, tc.mode, ctx.Mode()) + assert.Equal(t, tc.wantShort, ctx.ShortMode()) + }) + } +} + +func TestSetKubernetesMode_AKS(t *testing.T) { + ResetContext() + ctx := CurrentContext() + + ctx.SetKubernetesMode(config.ModeAKS) + assert.Equal(t, config.ModeAKS, ctx.KubernetesMode()) + assert.Equal(t, config.ShortModeAKS, ctx.ShortMode()) +} + +func TestSetKubernetesMode_ExistingModesUnchanged(t *testing.T) { + cases := map[string]struct { + mode string + wantShort string + }{ + "EKS": {config.ModeEKS, config.ShortModeEKS}, + "K8sEC2": {config.ModeK8sEC2, config.ShortModeK8sEC2}, + "K8sOnPrem": {config.ModeK8sOnPrem, config.ShortModeK8sOnPrem}, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + ResetContext() + ctx := CurrentContext() + ctx.SetKubernetesMode(tc.mode) + assert.Equal(t, tc.mode, ctx.KubernetesMode()) + assert.Equal(t, tc.wantShort, ctx.ShortMode()) + }) + } +} + +func TestSetKubernetesMode_UnknownClears(t *testing.T) { + ResetContext() + ctx := CurrentContext() + // A previously-set valid kubernetes mode is cleared by an unknown one. + ctx.SetKubernetesMode(config.ModeAKS) + ctx.SetKubernetesMode("not-a-real-mode") + assert.Equal(t, "", ctx.KubernetesMode()) + // shortMode is intentionally NOT cleared by the default branch: the normal + // non-Kubernetes path is SetMode(host) -> SetKubernetesMode("") and must keep + // the host shortMode set by SetMode. Here no host mode was set, so it retains + // the last valid kubernetes shortMode (ShortModeAKS). + assert.Equal(t, config.ShortModeAKS, ctx.ShortMode()) +} + +func TestSetKubernetesMode_EmptyPreservesHostShortMode(t *testing.T) { + ResetContext() + ctx := CurrentContext() + // Mimic the real startup ordering: host mode first, then the empty + // (non-Kubernetes) kubernetes mode. The host shortMode must survive. + ctx.SetMode(config.ModeAzureVM) + ctx.SetKubernetesMode("") + assert.Equal(t, "", ctx.KubernetesMode()) + assert.Equal(t, config.ShortModeAzureVM, ctx.ShortMode()) +} diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.conf b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.conf new file mode 100644 index 00000000000..c9bfdc1e818 --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.conf @@ -0,0 +1,15 @@ +[agent] + collection_jitter = "0s" + debug = false + flush_interval = "1s" + flush_jitter = "0s" + hostname = "" + interval = "60s" + logfile = "" + logtarget = "lumberjack" + metric_batch_size = 1000 + metric_buffer_limit = 10000 + omit_hostname = false + precision = "" + quiet = false + round_interval = false diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json new file mode 100644 index 00000000000..b0f6f9f64d6 --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json @@ -0,0 +1,12 @@ +{ + "agent": { + "region": "us-west-2" + }, + "opentelemetry": { + "collect": { + "host_metrics": { + "metrics_collection_interval": 10 + } + } + } +} diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml new file mode 100644 index 00000000000..51733eebf8e --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml @@ -0,0 +1,618 @@ +connectors: + forward/opentelemetry: {} +exporters: + otlphttp/metrics: + auth: + authenticator: agenthealth/opentelemetry_metrics + compression: gzip + encoding: proto + idle_conn_timeout: 1m30s + logs_endpoint: "" + max_idle_conns: 100 + metrics_endpoint: https://monitoring.us-west-2.amazonaws.com/v1/metrics + retry_on_failure: + enabled: true + initial_interval: 5s + max_elapsed_time: 5m0s + max_interval: 30s + multiplier: 1.5 + randomization_factor: 0.5 + sending_queue: + block_on_overflow: false + blocking: false + enabled: true + num_consumers: 10 + queue_size: 1000 + sizer: {} + wait_for_result: false + timeout: 30s + traces_endpoint: "" + write_buffer_size: 524288 +extensions: + agenthealth/opentelemetry_metrics: + additional_auth: sigv4auth/monitoring + is_usage_data_enabled: true + stats: + operations: + - '*' + usage_flags: + mode: AKS + region_type: ACJ + oidctoken: + output_token_file: /opt/aws/amazon-cloudwatch-agent/var/run/oidc-token + provider: azure + server: + listen_addr: :4311 + tls_ca_path: /etc/amazon-cloudwatch-observability-agent-client-cert/tls-ca.crt + tls_cert_path: /etc/amazon-cloudwatch-observability-agent-server-cert/server.crt + tls_key_path: /etc/amazon-cloudwatch-observability-agent-server-cert/server.key + sigv4auth/monitoring: + assume_role: {} + imds_retries: 1 + max_retries: 2 + num_workers: 8 + region: us-west-2 + request_timeout_seconds: 30 + service: monitoring +processors: + batch/opentelemetry_metrics: + metadata_cardinality_limit: 1000 + send_batch_max_size: 1000 + send_batch_size: 1000 + timeout: 30s + k8sattributes/opentelemetry: + auth_type: serviceAccount + context: "" + exclude: + pods: [] + extract: + annotations: + - from: pod + key: resource.opentelemetry.io/service.name + key_regex: "" + tag_name: resource.opentelemetry.io/service.name + - from: pod + key: resource.opentelemetry.io/service.namespace + key_regex: "" + tag_name: resource.opentelemetry.io/service.namespace + - from: pod + key: resource.opentelemetry.io/service.instance.id + key_regex: "" + tag_name: resource.opentelemetry.io/service.instance.id + - from: pod + key: resource.opentelemetry.io/service.version + key_regex: "" + tag_name: resource.opentelemetry.io/service.version + labels: + - from: pod + key: app.kubernetes.io/instance + key_regex: "" + tag_name: app.kubernetes.io/instance + - from: pod + key: app.kubernetes.io/name + key_regex: "" + tag_name: app.kubernetes.io/name + - from: pod + key: app.kubernetes.io/version + key_regex: "" + tag_name: app.kubernetes.io/version + metadata: + - k8s.namespace.name + - k8s.deployment.name + - k8s.replicaset.name + - k8s.statefulset.name + - k8s.daemonset.name + - k8s.job.name + - k8s.cronjob.name + - k8s.node.name + - k8s.pod.name + - k8s.pod.uid + - k8s.pod.start_time + - k8s.container.name + otel_annotations: false + filter: + namespace: "" + node: "" + node_from_env_var: K8S_NODE_NAME + kube_config_path: "" + passthrough: false + wait_for_metadata: false + wait_for_metadata_timeout: 10s + resourcedetection/opentelemetry: + aks: + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + k8s.cluster.name: + enabled: false + azure: + resource_attributes: + azure.resourcegroup.name: + enabled: true + azure.vm.name: + enabled: true + azure.vm.scaleset.name: + enabled: true + azure.vm.size: + enabled: true + cloud.account.id: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + tags: [] + consul: + address: "" + datacenter: "" + namespace: "" + resource_attributes: + cloud.region: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + token_file: "" + detectors: + - env + - aks + - azure + docker: + resource_attributes: + host.name: + enabled: true + os.type: + enabled: true + ec2: + fail_on_missing_metadata: false + max_attempts: 3 + max_backoff: 20s + resource_attributes: + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + host.id: + enabled: true + host.image.id: + enabled: true + host.name: + enabled: true + host.type: + enabled: true + tags: [] + ecs: + resource_attributes: + aws.ecs.cluster.arn: + enabled: true + aws.ecs.launchtype: + enabled: true + aws.ecs.task.arn: + enabled: true + aws.ecs.task.family: + enabled: true + aws.ecs.task.id: + enabled: true + aws.ecs.task.revision: + enabled: true + aws.log.group.arns: + enabled: true + aws.log.group.names: + enabled: true + aws.log.stream.arns: + enabled: true + aws.log.stream.names: + enabled: true + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + eks: + resource_attributes: + cloud.account.id: + enabled: false + cloud.platform: + enabled: true + cloud.provider: + enabled: true + k8s.cluster.name: + enabled: false + elasticbeanstalk: + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + deployment.environment: + enabled: true + service.instance.id: + enabled: true + service.version: + enabled: true + gcp: + resource_attributes: + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + faas.id: + enabled: true + faas.instance: + enabled: true + faas.name: + enabled: true + faas.version: + enabled: true + gcp.cloud_run.job.execution: + enabled: true + gcp.cloud_run.job.task_index: + enabled: true + gcp.gce.instance.hostname: + enabled: false + gcp.gce.instance.name: + enabled: false + gcp.gce.instance_group_manager.name: + enabled: true + gcp.gce.instance_group_manager.region: + enabled: true + gcp.gce.instance_group_manager.zone: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + host.type: + enabled: true + k8s.cluster.name: + enabled: true + heroku: + resource_attributes: + cloud.provider: + enabled: true + heroku.app.id: + enabled: true + heroku.dyno.id: + enabled: true + heroku.release.commit: + enabled: true + heroku.release.creation_timestamp: + enabled: true + service.instance.id: + enabled: true + service.name: + enabled: true + service.version: + enabled: true + idle_conn_timeout: 1m30s + k8snode: + auth_type: serviceAccount + context: "" + kube_config_path: "" + node_from_env_var: "" + resource_attributes: + k8s.node.name: + enabled: true + k8s.node.uid: + enabled: true + kubeadm: + auth_type: serviceAccount + context: "" + kube_config_path: "" + resource_attributes: + k8s.cluster.name: + enabled: true + k8s.cluster.uid: + enabled: true + lambda: + resource_attributes: + aws.log.group.names: + enabled: true + aws.log.stream.names: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + faas.instance: + enabled: true + faas.max_memory: + enabled: true + faas.name: + enabled: true + faas.version: + enabled: true + max_idle_conns: 100 + openshift: + address: "" + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + k8s.cluster.name: + enabled: true + tls: {} + token: "" + override: true + system: + resource_attributes: + host.arch: + enabled: false + host.cpu.cache.l2.size: + enabled: false + host.cpu.family: + enabled: false + host.cpu.model.id: + enabled: false + host.cpu.model.name: + enabled: false + host.cpu.stepping: + enabled: false + host.cpu.vendor.id: + enabled: false + host.id: + enabled: false + host.ip: + enabled: false + host.mac: + enabled: false + host.name: + enabled: true + os.description: + enabled: false + os.type: + enabled: true + os.version: + enabled: false + timeout: 2s + transform/host_metrics_scope: + error_mode: ignore + flatten_data: false + log_statements: + - context: scope + error_mode: ignore + statements: + - set(attributes["cloudwatch.source"], "cloudwatch-agent") + - set(attributes["cloudwatch.solution"], "otel-host-metrics") + metric_statements: + - context: scope + error_mode: ignore + statements: + - set(attributes["cloudwatch.source"], "cloudwatch-agent") + - set(attributes["cloudwatch.solution"], "otel-host-metrics") + trace_statements: + - context: scope + error_mode: ignore + statements: + - set(attributes["cloudwatch.source"], "cloudwatch-agent") + - set(attributes["cloudwatch.solution"], "otel-host-metrics") + transform/identity: + error_mode: ignore + flatten_data: false + log_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.namespace"], resource.attributes["resource.opentelemetry.io/service.namespace"]) where resource.attributes["service.namespace"] == nil and resource.attributes["resource.opentelemetry.io/service.namespace"] != nil + - set(resource.attributes["service.namespace"], resource.attributes["k8s.namespace.name"]) where resource.attributes["service.namespace"] == nil and resource.attributes["k8s.namespace.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["resource.opentelemetry.io/service.name"]) where resource.attributes["service.name"] == nil and resource.attributes["resource.opentelemetry.io/service.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["app.kubernetes.io/instance"]) where resource.attributes["service.name"] == nil and resource.attributes["app.kubernetes.io/instance"] != nil + - set(resource.attributes["service.name"], resource.attributes["app.kubernetes.io/name"]) where resource.attributes["service.name"] == nil and resource.attributes["app.kubernetes.io/name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.deployment.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.deployment.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.replicaset.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.replicaset.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.statefulset.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.statefulset.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.job.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.job.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.pod.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.pod.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.container.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.container.name"] != nil + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Format("arn:aws:eks:%s:%s:cluster/%s", [resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], resource.attributes["k8s.cluster.name"]])) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["k8s.cluster.name"] != nil and resource.attributes["cloud.platform"] == "aws_eks" + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.deployment.name"]) where resource.attributes["k8s.deployment.name"] != nil + - set(resource.attributes["k8s.workload.type"], "Deployment") where resource.attributes["k8s.deployment.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.statefulset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.statefulset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "StatefulSet") where resource.attributes["k8s.statefulset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.daemonset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.daemonset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "DaemonSet") where resource.attributes["k8s.daemonset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.job.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.job.name"] != nil + - set(resource.attributes["k8s.workload.type"], "Job") where resource.attributes["k8s.job.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.cronjob.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.cronjob.name"] != nil + - set(resource.attributes["k8s.workload.type"], "CronJob") where resource.attributes["k8s.cronjob.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.replicaset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.replicaset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "ReplicaSet") where resource.attributes["k8s.replicaset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["service.instance.id"], resource.attributes["resource.opentelemetry.io/service.instance.id"]) where resource.attributes["service.instance.id"] == nil and resource.attributes["resource.opentelemetry.io/service.instance.id"] != nil + - set(resource.attributes["service.instance.id"], Concat([resource.attributes["k8s.namespace.name"], resource.attributes["k8s.pod.name"], resource.attributes["k8s.container.name"]], "/")) where resource.attributes["service.instance.id"] == nil and resource.attributes["k8s.pod.name"] != nil + - set(resource.attributes["service.version"], resource.attributes["resource.opentelemetry.io/service.version"]) where resource.attributes["service.version"] == nil and resource.attributes["resource.opentelemetry.io/service.version"] != nil + - set(resource.attributes["service.version"], resource.attributes["app.kubernetes.io/version"]) where resource.attributes["service.version"] == nil and resource.attributes["app.kubernetes.io/version"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], Concat([resource.attributes["k8s.cluster.name"], resource.attributes["k8s.namespace.name"]], "/")], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil and resource.attributes["k8s.cluster.name"] != nil and resource.attributes["k8s.namespace.name"] != nil + - delete_key(resource.attributes, "resource.opentelemetry.io/service.namespace") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.name") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.instance.id") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.version") + - delete_key(resource.attributes, "app.kubernetes.io/instance") + - delete_key(resource.attributes, "app.kubernetes.io/name") + - delete_key(resource.attributes, "app.kubernetes.io/version") + metric_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.namespace"], resource.attributes["resource.opentelemetry.io/service.namespace"]) where resource.attributes["service.namespace"] == nil and resource.attributes["resource.opentelemetry.io/service.namespace"] != nil + - set(resource.attributes["service.namespace"], resource.attributes["k8s.namespace.name"]) where resource.attributes["service.namespace"] == nil and resource.attributes["k8s.namespace.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["resource.opentelemetry.io/service.name"]) where resource.attributes["service.name"] == nil and resource.attributes["resource.opentelemetry.io/service.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["app.kubernetes.io/instance"]) where resource.attributes["service.name"] == nil and resource.attributes["app.kubernetes.io/instance"] != nil + - set(resource.attributes["service.name"], resource.attributes["app.kubernetes.io/name"]) where resource.attributes["service.name"] == nil and resource.attributes["app.kubernetes.io/name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.deployment.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.deployment.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.replicaset.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.replicaset.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.statefulset.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.statefulset.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.job.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.job.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.pod.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.pod.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.container.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.container.name"] != nil + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Format("arn:aws:eks:%s:%s:cluster/%s", [resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], resource.attributes["k8s.cluster.name"]])) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["k8s.cluster.name"] != nil and resource.attributes["cloud.platform"] == "aws_eks" + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.deployment.name"]) where resource.attributes["k8s.deployment.name"] != nil + - set(resource.attributes["k8s.workload.type"], "Deployment") where resource.attributes["k8s.deployment.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.statefulset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.statefulset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "StatefulSet") where resource.attributes["k8s.statefulset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.daemonset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.daemonset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "DaemonSet") where resource.attributes["k8s.daemonset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.job.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.job.name"] != nil + - set(resource.attributes["k8s.workload.type"], "Job") where resource.attributes["k8s.job.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.cronjob.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.cronjob.name"] != nil + - set(resource.attributes["k8s.workload.type"], "CronJob") where resource.attributes["k8s.cronjob.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.replicaset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.replicaset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "ReplicaSet") where resource.attributes["k8s.replicaset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["service.instance.id"], resource.attributes["resource.opentelemetry.io/service.instance.id"]) where resource.attributes["service.instance.id"] == nil and resource.attributes["resource.opentelemetry.io/service.instance.id"] != nil + - set(resource.attributes["service.instance.id"], Concat([resource.attributes["k8s.namespace.name"], resource.attributes["k8s.pod.name"], resource.attributes["k8s.container.name"]], "/")) where resource.attributes["service.instance.id"] == nil and resource.attributes["k8s.pod.name"] != nil + - set(resource.attributes["service.version"], resource.attributes["resource.opentelemetry.io/service.version"]) where resource.attributes["service.version"] == nil and resource.attributes["resource.opentelemetry.io/service.version"] != nil + - set(resource.attributes["service.version"], resource.attributes["app.kubernetes.io/version"]) where resource.attributes["service.version"] == nil and resource.attributes["app.kubernetes.io/version"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], Concat([resource.attributes["k8s.cluster.name"], resource.attributes["k8s.namespace.name"]], "/")], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil and resource.attributes["k8s.cluster.name"] != nil and resource.attributes["k8s.namespace.name"] != nil + - delete_key(resource.attributes, "resource.opentelemetry.io/service.namespace") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.name") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.instance.id") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.version") + - delete_key(resource.attributes, "app.kubernetes.io/instance") + - delete_key(resource.attributes, "app.kubernetes.io/name") + - delete_key(resource.attributes, "app.kubernetes.io/version") + trace_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.namespace"], resource.attributes["resource.opentelemetry.io/service.namespace"]) where resource.attributes["service.namespace"] == nil and resource.attributes["resource.opentelemetry.io/service.namespace"] != nil + - set(resource.attributes["service.namespace"], resource.attributes["k8s.namespace.name"]) where resource.attributes["service.namespace"] == nil and resource.attributes["k8s.namespace.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["resource.opentelemetry.io/service.name"]) where resource.attributes["service.name"] == nil and resource.attributes["resource.opentelemetry.io/service.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["app.kubernetes.io/instance"]) where resource.attributes["service.name"] == nil and resource.attributes["app.kubernetes.io/instance"] != nil + - set(resource.attributes["service.name"], resource.attributes["app.kubernetes.io/name"]) where resource.attributes["service.name"] == nil and resource.attributes["app.kubernetes.io/name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.deployment.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.deployment.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.replicaset.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.replicaset.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.statefulset.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.statefulset.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.job.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.job.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.pod.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.pod.name"] != nil + - set(resource.attributes["service.name"], resource.attributes["k8s.container.name"]) where resource.attributes["service.name"] == nil and resource.attributes["k8s.container.name"] != nil + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Format("arn:aws:eks:%s:%s:cluster/%s", [resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], resource.attributes["k8s.cluster.name"]])) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["k8s.cluster.name"] != nil and resource.attributes["cloud.platform"] == "aws_eks" + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.deployment.name"]) where resource.attributes["k8s.deployment.name"] != nil + - set(resource.attributes["k8s.workload.type"], "Deployment") where resource.attributes["k8s.deployment.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.statefulset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.statefulset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "StatefulSet") where resource.attributes["k8s.statefulset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.daemonset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.daemonset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "DaemonSet") where resource.attributes["k8s.daemonset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.job.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.job.name"] != nil + - set(resource.attributes["k8s.workload.type"], "Job") where resource.attributes["k8s.job.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.cronjob.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.cronjob.name"] != nil + - set(resource.attributes["k8s.workload.type"], "CronJob") where resource.attributes["k8s.cronjob.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["k8s.workload.name"], resource.attributes["k8s.replicaset.name"]) where resource.attributes["k8s.workload.name"] == nil and resource.attributes["k8s.replicaset.name"] != nil + - set(resource.attributes["k8s.workload.type"], "ReplicaSet") where resource.attributes["k8s.replicaset.name"] != nil and resource.attributes["k8s.workload.type"] == nil + - set(resource.attributes["service.instance.id"], resource.attributes["resource.opentelemetry.io/service.instance.id"]) where resource.attributes["service.instance.id"] == nil and resource.attributes["resource.opentelemetry.io/service.instance.id"] != nil + - set(resource.attributes["service.instance.id"], Concat([resource.attributes["k8s.namespace.name"], resource.attributes["k8s.pod.name"], resource.attributes["k8s.container.name"]], "/")) where resource.attributes["service.instance.id"] == nil and resource.attributes["k8s.pod.name"] != nil + - set(resource.attributes["service.version"], resource.attributes["resource.opentelemetry.io/service.version"]) where resource.attributes["service.version"] == nil and resource.attributes["resource.opentelemetry.io/service.version"] != nil + - set(resource.attributes["service.version"], resource.attributes["app.kubernetes.io/version"]) where resource.attributes["service.version"] == nil and resource.attributes["app.kubernetes.io/version"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], Concat([resource.attributes["k8s.cluster.name"], resource.attributes["k8s.namespace.name"]], "/")], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil and resource.attributes["k8s.cluster.name"] != nil and resource.attributes["k8s.namespace.name"] != nil + - delete_key(resource.attributes, "resource.opentelemetry.io/service.namespace") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.name") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.instance.id") + - delete_key(resource.attributes, "resource.opentelemetry.io/service.version") + - delete_key(resource.attributes, "app.kubernetes.io/instance") + - delete_key(resource.attributes, "app.kubernetes.io/name") + - delete_key(resource.attributes, "app.kubernetes.io/version") +receivers: + hostmetrics/opentelemetry: + collection_interval: 10s + initial_delay: 1s + scrapers: + cpu: + metrics: + system.cpu.frequency: + enabled: true + system.cpu.logical.count: + enabled: true + system.cpu.physical.count: + enabled: true + system.cpu.utilization: + enabled: true + disk: null + filesystem: + metrics: + system.filesystem.utilization: + enabled: true + load: null + memory: + metrics: + system.linux.memory.available: + enabled: true + system.linux.memory.dirty: + enabled: true + system.memory.limit: + enabled: true + system.memory.page_size: + enabled: true + system.memory.utilization: + enabled: true + network: null + processes: null + timeout: 0s +service: + extensions: + - sigv4auth/monitoring + - agenthealth/opentelemetry_metrics + - server + - oidctoken + pipelines: + metrics/host_metrics: + exporters: + - forward/opentelemetry + processors: + - transform/host_metrics_scope + receivers: + - hostmetrics/opentelemetry + metrics/opentelemetry: + exporters: + - otlphttp/metrics + processors: + - resourcedetection/opentelemetry + - k8sattributes/opentelemetry + - transform/identity + - batch/opentelemetry_metrics + receivers: + - forward/opentelemetry + telemetry: + logs: + encoding: console + level: info + sampling: + enabled: true + initial: 2 + thereafter: 500 + tick: 10s + metrics: + level: None + traces: + level: None diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.conf b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.conf new file mode 100644 index 00000000000..9d2168ce61a --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.conf @@ -0,0 +1,15 @@ +[agent] + collection_jitter = "0s" + debug = false + flush_interval = "1s" + flush_jitter = "0s" + hostname = "" + interval = "60s" + logfile = "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log" + logtarget = "lumberjack" + metric_batch_size = 1000 + metric_buffer_limit = 10000 + omit_hostname = false + precision = "" + quiet = false + round_interval = false diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json new file mode 100644 index 00000000000..b0f6f9f64d6 --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json @@ -0,0 +1,12 @@ +{ + "agent": { + "region": "us-west-2" + }, + "opentelemetry": { + "collect": { + "host_metrics": { + "metrics_collection_interval": 10 + } + } + } +} diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml new file mode 100644 index 00000000000..30ba4587017 --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml @@ -0,0 +1,455 @@ +connectors: + forward/opentelemetry: {} +exporters: + otlphttp/metrics: + auth: + authenticator: agenthealth/opentelemetry_metrics + compression: gzip + encoding: proto + idle_conn_timeout: 1m30s + logs_endpoint: "" + max_idle_conns: 100 + metrics_endpoint: https://monitoring.us-west-2.amazonaws.com/v1/metrics + retry_on_failure: + enabled: true + initial_interval: 5s + max_elapsed_time: 5m0s + max_interval: 30s + multiplier: 1.5 + randomization_factor: 0.5 + sending_queue: + block_on_overflow: false + blocking: false + enabled: true + num_consumers: 10 + queue_size: 1000 + sizer: {} + wait_for_result: false + timeout: 30s + traces_endpoint: "" + write_buffer_size: 524288 +extensions: + agenthealth/opentelemetry_metrics: + additional_auth: sigv4auth/monitoring + is_usage_data_enabled: true + stats: + operations: + - '*' + usage_flags: + mode: AZVM + region_type: ACJ + oidctoken: + output_token_file: /opt/aws/amazon-cloudwatch-agent/var/run/oidc-token + provider: azure + sigv4auth/monitoring: + assume_role: {} + imds_retries: 1 + max_retries: 2 + num_workers: 8 + region: us-west-2 + request_timeout_seconds: 30 + service: monitoring +processors: + batch/opentelemetry_metrics: + metadata_cardinality_limit: 1000 + send_batch_max_size: 1000 + send_batch_size: 1000 + timeout: 30s + resourcedetection/opentelemetry: + aks: + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + k8s.cluster.name: + enabled: false + azure: + resource_attributes: + azure.resourcegroup.name: + enabled: true + azure.vm.name: + enabled: true + azure.vm.scaleset.name: + enabled: true + azure.vm.size: + enabled: true + cloud.account.id: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + tags: [] + consul: + address: "" + datacenter: "" + namespace: "" + resource_attributes: + cloud.region: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + token_file: "" + detectors: + - env + - azure + docker: + resource_attributes: + host.name: + enabled: true + os.type: + enabled: true + ec2: + fail_on_missing_metadata: false + max_attempts: 3 + max_backoff: 20s + resource_attributes: + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + host.id: + enabled: true + host.image.id: + enabled: true + host.name: + enabled: true + host.type: + enabled: true + tags: [] + ecs: + resource_attributes: + aws.ecs.cluster.arn: + enabled: true + aws.ecs.launchtype: + enabled: true + aws.ecs.task.arn: + enabled: true + aws.ecs.task.family: + enabled: true + aws.ecs.task.id: + enabled: true + aws.ecs.task.revision: + enabled: true + aws.log.group.arns: + enabled: true + aws.log.group.names: + enabled: true + aws.log.stream.arns: + enabled: true + aws.log.stream.names: + enabled: true + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + eks: + resource_attributes: + cloud.account.id: + enabled: false + cloud.platform: + enabled: true + cloud.provider: + enabled: true + k8s.cluster.name: + enabled: false + elasticbeanstalk: + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + deployment.environment: + enabled: true + service.instance.id: + enabled: true + service.version: + enabled: true + gcp: + resource_attributes: + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + faas.id: + enabled: true + faas.instance: + enabled: true + faas.name: + enabled: true + faas.version: + enabled: true + gcp.cloud_run.job.execution: + enabled: true + gcp.cloud_run.job.task_index: + enabled: true + gcp.gce.instance.hostname: + enabled: false + gcp.gce.instance.name: + enabled: false + gcp.gce.instance_group_manager.name: + enabled: true + gcp.gce.instance_group_manager.region: + enabled: true + gcp.gce.instance_group_manager.zone: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + host.type: + enabled: true + k8s.cluster.name: + enabled: true + heroku: + resource_attributes: + cloud.provider: + enabled: true + heroku.app.id: + enabled: true + heroku.dyno.id: + enabled: true + heroku.release.commit: + enabled: true + heroku.release.creation_timestamp: + enabled: true + service.instance.id: + enabled: true + service.name: + enabled: true + service.version: + enabled: true + idle_conn_timeout: 1m30s + k8snode: + auth_type: serviceAccount + context: "" + kube_config_path: "" + node_from_env_var: "" + resource_attributes: + k8s.node.name: + enabled: true + k8s.node.uid: + enabled: true + kubeadm: + auth_type: serviceAccount + context: "" + kube_config_path: "" + resource_attributes: + k8s.cluster.name: + enabled: true + k8s.cluster.uid: + enabled: true + lambda: + resource_attributes: + aws.log.group.names: + enabled: true + aws.log.stream.names: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + faas.instance: + enabled: true + faas.max_memory: + enabled: true + faas.name: + enabled: true + faas.version: + enabled: true + max_idle_conns: 100 + openshift: + address: "" + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + k8s.cluster.name: + enabled: true + tls: {} + token: "" + override: true + system: + resource_attributes: + host.arch: + enabled: false + host.cpu.cache.l2.size: + enabled: false + host.cpu.family: + enabled: false + host.cpu.model.id: + enabled: false + host.cpu.model.name: + enabled: false + host.cpu.stepping: + enabled: false + host.cpu.vendor.id: + enabled: false + host.id: + enabled: false + host.ip: + enabled: false + host.mac: + enabled: false + host.name: + enabled: true + os.description: + enabled: false + os.type: + enabled: true + os.version: + enabled: false + timeout: 2s + transform/host_metrics_scope: + error_mode: ignore + flatten_data: false + log_statements: + - context: scope + error_mode: ignore + statements: + - set(attributes["cloudwatch.source"], "cloudwatch-agent") + - set(attributes["cloudwatch.solution"], "otel-host-metrics") + metric_statements: + - context: scope + error_mode: ignore + statements: + - set(attributes["cloudwatch.source"], "cloudwatch-agent") + - set(attributes["cloudwatch.solution"], "otel-host-metrics") + trace_statements: + - context: scope + error_mode: ignore + statements: + - set(attributes["cloudwatch.source"], "cloudwatch-agent") + - set(attributes["cloudwatch.solution"], "otel-host-metrics") + transform/identity: + error_mode: ignore + flatten_data: false + log_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil + metric_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil + trace_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil +receivers: + hostmetrics/opentelemetry: + collection_interval: 10s + initial_delay: 1s + scrapers: + cpu: + metrics: + system.cpu.frequency: + enabled: true + system.cpu.logical.count: + enabled: true + system.cpu.physical.count: + enabled: true + system.cpu.utilization: + enabled: true + disk: null + filesystem: + metrics: + system.filesystem.utilization: + enabled: true + load: null + memory: + metrics: + system.linux.memory.available: + enabled: true + system.linux.memory.dirty: + enabled: true + system.memory.limit: + enabled: true + system.memory.page_size: + enabled: true + system.memory.utilization: + enabled: true + network: null + processes: null + timeout: 0s +service: + extensions: + - sigv4auth/monitoring + - agenthealth/opentelemetry_metrics + - oidctoken + pipelines: + metrics/host_metrics: + exporters: + - forward/opentelemetry + processors: + - transform/host_metrics_scope + receivers: + - hostmetrics/opentelemetry + metrics/opentelemetry: + exporters: + - otlphttp/metrics + processors: + - resourcedetection/opentelemetry + - transform/identity + - batch/opentelemetry_metrics + receivers: + - forward/opentelemetry + telemetry: + logs: + encoding: console + level: info + output_paths: + - /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log + sampling: + enabled: true + initial: 2 + thereafter: 500 + tick: 10s + metrics: + level: None + traces: + level: None diff --git a/translator/tocwconfig/tocwconfig_test.go b/translator/tocwconfig/tocwconfig_test.go index e81c57cd1cf..358d766c5f3 100644 --- a/translator/tocwconfig/tocwconfig_test.go +++ b/translator/tocwconfig/tocwconfig_test.go @@ -132,6 +132,25 @@ func TestAppSignalsAndEKSConfig(t *testing.T) { checkTranslation(t, "appsignals_and_eks_config", "windows", expectedEnvVars, "") } +func TestAzureVMHostMetricsConfig(t *testing.T) { + resetContext(t) + context.CurrentContext().SetMode(config.ModeAzureVM) + + checkTranslation(t, "opentelemetry/host_metrics_azurevm_config", "linux", nil, "") +} + +func TestAKSHostMetricsConfig(t *testing.T) { + resetContext(t) + context.CurrentContext().SetRunInContainer(true) + t.Setenv(envconfig.RunInAKS, envconfig.TrueValue) + // AKS nodes are Azure VMs, so DetectAgentMode resolves the host mode to + // AzureVM; mirror that here so the host-level config is generated correctly. + context.CurrentContext().SetMode(config.ModeAzureVM) + context.CurrentContext().SetKubernetesMode(config.ModeAKS) + + checkTranslation(t, "opentelemetry/host_metrics_aks_config", "linux", nil, "") +} + func TestAppSignalsFallbackAndEKSConfig(t *testing.T) { resetContext(t) context.CurrentContext().SetRunInContainer(true) diff --git a/translator/translate/otel/extension/oidctoken/translator.go b/translator/translate/otel/extension/oidctoken/translator.go new file mode 100644 index 00000000000..cb350853fe2 --- /dev/null +++ b/translator/translate/otel/extension/oidctoken/translator.go @@ -0,0 +1,52 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package oidctoken + +import ( + "path/filepath" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidctokenextension" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/extension" + + "github.com/aws/amazon-cloudwatch-agent/tool/paths" + "github.com/aws/amazon-cloudwatch-agent/translator/config" + "github.com/aws/amazon-cloudwatch-agent/translator/context" + "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" +) + +// defaultOutputTokenFile is where the extension writes the OIDC token, under the +// agent dir so a sibling auth component (e.g. sigv4auth) can read the same path. +var defaultOutputTokenFile = filepath.Join(paths.AgentDir, "var", "run", "oidc-token") + +type translator struct { + factory extension.Factory +} + +var _ common.ComponentTranslator = (*translator)(nil) + +// NewTranslator returns the oidctoken extension translator (Azure provider). +func NewTranslator() common.ComponentTranslator { + return &translator{factory: oidctokenextension.NewFactory()} +} + +func (t *translator) ID() component.ID { + return component.NewID(t.factory.Type()) +} + +func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { + cfg := t.factory.CreateDefaultConfig().(*oidctokenextension.Config) + cfg.Provider = providerForMode(context.CurrentContext().Mode()) + cfg.OutputTokenFile = defaultOutputTokenFile + return cfg, nil +} + +// providerForMode maps the detected platform mode to the OIDC token provider. +func providerForMode(mode string) oidctokenextension.ProviderType { + if mode == config.ModeAzureVM { + return oidctokenextension.ProviderAzure + } + return oidctokenextension.ProviderAuto +} diff --git a/translator/translate/otel/extension/oidctoken/translator_test.go b/translator/translate/otel/extension/oidctoken/translator_test.go new file mode 100644 index 00000000000..f83600126cc --- /dev/null +++ b/translator/translate/otel/extension/oidctoken/translator_test.go @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package oidctoken + +import ( + "testing" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidctokenextension" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/confmap" + + "github.com/aws/amazon-cloudwatch-agent/translator/config" + "github.com/aws/amazon-cloudwatch-agent/translator/context" +) + +func TestTranslator(t *testing.T) { + t.Cleanup(context.ResetContext) + context.ResetContext() + context.CurrentContext().SetMode(config.ModeAzureVM) + + tt := NewTranslator() + assert.Equal(t, "oidctoken", tt.ID().String()) + + got, err := tt.Translate(confmap.New()) + require.NoError(t, err) + require.NotNil(t, got) + + cfg, ok := got.(*oidctokenextension.Config) + require.True(t, ok) + assert.Equal(t, oidctokenextension.ProviderAzure, cfg.Provider) + assert.Equal(t, defaultOutputTokenFile, cfg.OutputTokenFile) + assert.NoError(t, cfg.Validate()) +} + +func TestProviderForMode(t *testing.T) { + assert.Equal(t, oidctokenextension.ProviderAzure, providerForMode(config.ModeAzureVM)) + // Any non-AzureVM mode defers to the extension's own environment detection. + assert.Equal(t, oidctokenextension.ProviderAuto, providerForMode(config.ModeEC2)) + assert.Equal(t, oidctokenextension.ProviderAuto, providerForMode(config.ModeOnPrem)) +} diff --git a/translator/translate/otel/processor/resourcedetection/configs/aks_config.yaml b/translator/translate/otel/processor/resourcedetection/configs/aks_config.yaml new file mode 100644 index 00000000000..f72397d4e0c --- /dev/null +++ b/translator/translate/otel/processor/resourcedetection/configs/aks_config.yaml @@ -0,0 +1,3 @@ +detectors: [env, aks, azure] +override: true +timeout: 2s diff --git a/translator/translate/otel/processor/resourcedetection/configs/azure_config.yaml b/translator/translate/otel/processor/resourcedetection/configs/azure_config.yaml new file mode 100644 index 00000000000..2951efdb403 --- /dev/null +++ b/translator/translate/otel/processor/resourcedetection/configs/azure_config.yaml @@ -0,0 +1,3 @@ +detectors: [env, azure] +override: true +timeout: 2s diff --git a/translator/translate/otel/processor/resourcedetection/translator.go b/translator/translate/otel/processor/resourcedetection/translator.go index d5a631276d3..a76d592fd3e 100644 --- a/translator/translate/otel/processor/resourcedetection/translator.go +++ b/translator/translate/otel/processor/resourcedetection/translator.go @@ -25,6 +25,12 @@ var appSignalsDefaultResourceDetectionConfig string //go:embed configs/ecs_config.yaml var appSignalsECSResourceDetectionConfig string +//go:embed configs/azure_config.yaml +var azureVMResourceDetectionConfig string + +//go:embed configs/aks_config.yaml +var aksResourceDetectionConfig string + type translator struct { name string signal pipeline.Signal @@ -88,6 +94,10 @@ func (t *translator) Translate(conf *confmap.Conf) (component.Config, error) { switch mode { case config.ModeECS: return common.GetYamlFileToYamlConfig(cfg, appSignalsECSResourceDetectionConfig) + case config.ModeAKS: + return common.GetYamlFileToYamlConfig(cfg, aksResourceDetectionConfig) + case config.ModeAzureVM: + return common.GetYamlFileToYamlConfig(cfg, azureVMResourceDetectionConfig) default: return common.GetYamlFileToYamlConfig(cfg, appSignalsDefaultResourceDetectionConfig) } diff --git a/translator/translate/otel/processor/resourcedetection/translator_test.go b/translator/translate/otel/processor/resourcedetection/translator_test.go index cd2c60c56b4..2b6ca771244 100644 --- a/translator/translate/otel/processor/resourcedetection/translator_test.go +++ b/translator/translate/otel/processor/resourcedetection/translator_test.go @@ -20,11 +20,12 @@ import ( func TestTranslate(t *testing.T) { tt := NewTranslator(WithSignal(pipeline.SignalTraces)) testCases := map[string]struct { - input map[string]interface{} - mode string - isECS bool - want *confmap.Conf - wantErr error + input map[string]interface{} + mode string + kubernetesMode string + isECS bool + want *confmap.Conf + wantErr error }{ "WithAppSignalsEnabledOnECS": { mode: translatorconfig.ModeEC2, @@ -118,11 +119,53 @@ func TestTranslate(t *testing.T) { }, }), }, + "WithAppSignalsEnabledOnAzureVM": { + mode: translatorconfig.ModeAzureVM, + input: map[string]interface{}{ + "traces": map[string]interface{}{ + "traces_collected": map[string]interface{}{ + "app_signals": map[string]interface{}{}, + }, + }}, + want: confmap.NewFromStringMap(map[string]interface{}{ + "detectors": []interface{}{ + "env", + "azure", + }, + "timeout": "2s", + "override": true, + }), + }, + "WithAppSignalsEnabledOnAKS": { + kubernetesMode: translatorconfig.ModeAKS, + input: map[string]interface{}{ + "traces": map[string]interface{}{ + "traces_collected": map[string]interface{}{ + "app_signals": map[string]interface{}{}, + }, + }}, + want: confmap.NewFromStringMap(map[string]interface{}{ + "detectors": []interface{}{ + "env", + "aks", + "azure", + }, + "timeout": "2s", + "override": true, + }), + }, } factory := resourcedetectionprocessor.NewFactory() for name, testCase := range testCases { t.Run(name, func(t *testing.T) { - context.CurrentContext().SetMode(testCase.mode) + // Reset so kubernetesMode/mode does not bleed across cases (map + // iteration order is non-deterministic). + context.ResetContext() + if testCase.kubernetesMode != "" { + context.CurrentContext().SetKubernetesMode(testCase.kubernetesMode) + } else { + context.CurrentContext().SetMode(testCase.mode) + } if testCase.isECS { ecsutil.GetECSUtilSingleton().Region = "test-region" } else { diff --git a/translator/translate/otel/translate_otel.go b/translator/translate/otel/translate_otel.go index a6518a7d6ea..e4bef928a2b 100644 --- a/translator/translate/otel/translate_otel.go +++ b/translator/translate/otel/translate_otel.go @@ -20,9 +20,11 @@ import ( "go.uber.org/multierr" "go.uber.org/zap/zapcore" + "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/entitystore" + "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/oidctoken" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/server" pipelinetranslator "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline/applicationsignals" @@ -113,6 +115,11 @@ func translateInternal(jsonConfig interface{}, os string, validate bool) (*otelc pipelines.Translators.Extensions.Set(server.NewTranslator()) } + // Azure VM has no AWS credentials, so it needs oidctoken for egress auth. + if context.CurrentContext().Mode() == config.ModeAzureVM { + pipelines.Translators.Extensions.Set(oidctoken.NewTranslator()) + } + cfg := &otelcol.Config{ Receivers: map[component.ID]component.Config{}, Exporters: map[component.ID]component.Config{}, diff --git a/translator/util/azuredetector/azuredetector.go b/translator/util/azuredetector/azuredetector.go new file mode 100644 index 00000000000..ef96cf532b2 --- /dev/null +++ b/translator/util/azuredetector/azuredetector.go @@ -0,0 +1,84 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +// Package azuredetector detects whether the CloudWatch agent is running on +// Azure VM or Azure Kubernetes Service. Mirrors the eksdetector package. +package azuredetector + +import ( + "context" + "sync" + "time" + + azuremeta "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/metadataproviders/azure" + + "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" +) + +// azureIMDSTimeout bounds one probe attempt so startup can't stall on a non-Azure host. +const azureIMDSTimeout = 1 * time.Second + +// azureIMDSMaxAttempts retries transient probe errors; a definitive result is not retried. +const azureIMDSMaxAttempts = 2 + +// IsAzureVMCache holds the cached result of the Azure VM detection probe. +type IsAzureVMCache struct { + Value bool + Err error +} + +var ( + // metadataProvider is the Azure IMDS provider; overridable in tests. + metadataProvider = azuremeta.NewProvider() + probeAzureIMDS = defaultProbeAzureIMDS + isRunningInAKS = envconfig.IsRunningInAKS + + // Public detection entry points; overridable in tests (mirrors eksdetector.IsEKS). + IsAzureVM = isAzureVM + IsAKS = isAKS + + // Azure VM detection cache; mutex (not sync.Once) so tests can reset it safely. + azureVMMu sync.Mutex + azureVMResolved bool + azureVMCache IsAzureVMCache +) + +// isAKS reports whether RUN_IN_AKS is set (by the AKS Helm chart); no I/O. +func isAKS() bool { + return isRunningInAKS() +} + +// isAzureVM probes Azure IMDS (cached). An unreachable probe reports false. +func isAzureVM() IsAzureVMCache { + azureVMMu.Lock() + defer azureVMMu.Unlock() + if !azureVMResolved { + value, err := probeAzureIMDS() + result := IsAzureVMCache{Value: value, Err: err} + // Cache only a definitive answer so a boot-time transient error can re-probe. + if err == nil { + azureVMCache = result + azureVMResolved = true + } + return result + } + return azureVMCache +} + +// defaultProbeAzureIMDS queries the Azure IMDS via the contrib metadata provider, +// returning true when a compute document with a VM ID is returned. The provider +// error is transient (IMDS unreachable), so it is retried but never cached. +func defaultProbeAzureIMDS() (bool, error) { + var lastErr error + for attempt := 0; attempt < azureIMDSMaxAttempts; attempt++ { + ctx, cancel := context.WithTimeout(context.Background(), azureIMDSTimeout) + compute, err := metadataProvider.Metadata(ctx) + cancel() + if err == nil { + // A genuine Azure compute document always carries a VM ID. + return compute != nil && compute.VMID != "", nil + } + lastErr = err + } + return false, lastErr +} diff --git a/translator/util/azuredetector/azuredetector_test.go b/translator/util/azuredetector/azuredetector_test.go new file mode 100644 index 00000000000..8b3ae380953 --- /dev/null +++ b/translator/util/azuredetector/azuredetector_test.go @@ -0,0 +1,152 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package azuredetector + +import ( + "context" + "errors" + "testing" + + azuremeta "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/metadataproviders/azure" + "github.com/stretchr/testify/assert" + + "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" +) + +// fakeProvider is a stub azuremeta.Provider for tests. +type fakeProvider struct { + compute *azuremeta.ComputeMetadata + err error + calls int +} + +func (f *fakeProvider) Metadata(context.Context) (*azuremeta.ComputeMetadata, error) { + f.calls++ + return f.compute, f.err +} + +// setProvider swaps metadataProvider for the test and resets the cache, +// restoring both on cleanup. +func setProvider(t *testing.T, p azuremeta.Provider) { + t.Helper() + orig := metadataProvider + metadataProvider = p + resetCacheForTesting() + t.Cleanup(func() { + metadataProvider = orig + resetCacheForTesting() + }) +} + +// resetCacheForTesting clears the Azure VM detection cache under its lock. +func resetCacheForTesting() { + azureVMMu.Lock() + defer azureVMMu.Unlock() + azureVMCache = IsAzureVMCache{} + azureVMResolved = false +} + +// ---- AKS (env-var) detection ---- + +func TestIsAKS_EnvSet(t *testing.T) { + orig := isRunningInAKS + t.Cleanup(func() { isRunningInAKS = orig }) + isRunningInAKS = func() bool { return true } + assert.True(t, isAKS()) +} + +func TestIsAKS_EnvUnset(t *testing.T) { + orig := isRunningInAKS + t.Cleanup(func() { isRunningInAKS = orig }) + isRunningInAKS = func() bool { return false } + assert.False(t, isAKS()) +} + +func TestIsAKS_ReadsRunInAKSEnvVar(t *testing.T) { + // Exercise the real envconfig-backed check to confirm the wiring to RUN_IN_AKS. + t.Setenv(envconfig.RunInAKS, envconfig.TrueValue) + assert.True(t, isAKS()) + + t.Setenv(envconfig.RunInAKS, "") + assert.False(t, isAKS()) +} + +// ---- Azure VM (IMDS) detection ---- + +func TestIsAzureVM_Detected(t *testing.T) { + setProvider(t, &fakeProvider{compute: &azuremeta.ComputeMetadata{VMID: "vm-123", Name: "vm1"}}) + + result := isAzureVM() + assert.True(t, result.Value) + assert.NoError(t, result.Err) +} + +func TestIsAzureVM_NotAzure(t *testing.T) { + // The provider succeeds but returns no VM ID (e.g. a non-Azure 200 response). + cases := map[string]*azuremeta.ComputeMetadata{ + "nilCompute": nil, + "emptyCompute": {}, + } + for name, compute := range cases { + t.Run(name, func(t *testing.T) { + setProvider(t, &fakeProvider{compute: compute}) + result := isAzureVM() + assert.False(t, result.Value) + assert.NoError(t, result.Err) + }) + } +} + +func TestIsAzureVM_Unreachable(t *testing.T) { + // IMDS unreachable (e.g. on-prem/EC2): the probe errors and reports false. + p := &fakeProvider{err: errors.New("dial tcp 169.254.169.254: connect: no route to host")} + setProvider(t, p) + + result := isAzureVM() + assert.False(t, result.Value) + assert.Error(t, result.Err) + assert.Equal(t, azureIMDSMaxAttempts, p.calls, "a transient error should be retried") +} + +func TestIsAzureVM_TransientErrorNotCached(t *testing.T) { + // A probe that exhausts retries with a transient error must NOT cache a + // negative: a later call (IMDS now up) must be able to re-probe. + p := &transientThenOKProvider{failFirst: azureIMDSMaxAttempts} + setProvider(t, p) + + first := isAzureVM() + assert.False(t, first.Value) + assert.Error(t, first.Err) + assert.Equal(t, azureIMDSMaxAttempts, p.calls, "first call should exhaust all retry attempts") + + second := isAzureVM() + assert.True(t, second.Value, "a transient failure must not be cached; re-probe should succeed") + assert.NoError(t, second.Err) + assert.Equal(t, azureIMDSMaxAttempts+1, p.calls, "second call should require exactly one successful probe") +} + +func TestIsAzureVM_ResultIsCached(t *testing.T) { + p := &fakeProvider{compute: &azuremeta.ComputeMetadata{VMID: "vm-123"}} + setProvider(t, p) + + first := isAzureVM() + second := isAzureVM() + assert.True(t, first.Value) + assert.Equal(t, first, second) + assert.Equal(t, 1, p.calls, "IMDS should be queried at most once due to caching") +} + +// transientThenOKProvider errors for the first failFirst calls, then succeeds. +type transientThenOKProvider struct { + failFirst int + calls int +} + +func (p *transientThenOKProvider) Metadata(context.Context) (*azuremeta.ComputeMetadata, error) { + p.calls++ + if p.calls <= p.failFirst { + return nil, errors.New("imds temporarily unavailable") + } + return &azuremeta.ComputeMetadata{VMID: "vm-123"}, nil +} diff --git a/translator/util/sdkutil.go b/translator/util/sdkutil.go index d0bb20510b7..94bd873d11c 100644 --- a/translator/util/sdkutil.go +++ b/translator/util/sdkutil.go @@ -15,6 +15,7 @@ import ( "github.com/aws/amazon-cloudwatch-agent/cfg/commonconfig" "github.com/aws/amazon-cloudwatch-agent/translator" "github.com/aws/amazon-cloudwatch-agent/translator/config" + "github.com/aws/amazon-cloudwatch-agent/translator/util/azuredetector" "github.com/aws/amazon-cloudwatch-agent/translator/util/ec2util" "github.com/aws/amazon-cloudwatch-agent/translator/util/ecsutil" "github.com/aws/amazon-cloudwatch-agent/translator/util/eksdetector" @@ -29,6 +30,10 @@ var DetectCredentialsPath = detectCredentialsPath var DefaultEC2Region = defaultEC2Region var DefaultECSRegion = defaultECSRegion var IsEKS = isEKS + +// IsAKS/IsAzureVM are overridable detection hooks; tests override these vars. +var IsAKS = isAKS +var IsAzureVM = isAzureVM var runInAws = os.Getenv(config.RUN_IN_AWS) var runWithIrsa = os.Getenv(config.RUN_WITH_IRSA) @@ -57,6 +62,20 @@ func DetectAgentMode(configuredMode string) string { return config.ModeEC2 } + // Azure is checked only after all AWS signals, so it can't override them. + // AKS nodes are Azure VMs; RUN_IN_AKS is checked before the IMDS probe since + // an AKS pod may not reach IMDS from its container. + if IsAKS() { + fmt.Println("I! Detected from ENV instance is Azure (AKS)") + return config.ModeAzureVM + } + + // Last resort: IMDS probe (~2s worst-case one-time cost on a black-holed host). + if IsAzureVM() { + fmt.Println("I! Detected the instance is Azure VM") + return config.ModeAzureVM + } + fmt.Println("I! Detected the instance is OnPremise") return config.ModeOnPrem } @@ -64,12 +83,18 @@ func DetectAgentMode(configuredMode string) string { func DetectKubernetesMode(configuredMode string) string { isEKS := IsEKS() - if isEKS.Err != nil { - return "" // not kubernetes + // EKS wins over AKS when confirmed. + if isEKS.Err == nil && isEKS.Value { + return config.ModeEKS } - if isEKS.Value { - return config.ModeEKS + // AKS is an explicit env signal, so it applies even if the EKS probe errored. + if IsAKS() { + return config.ModeAKS + } + + if isEKS.Err != nil { + return "" // not kubernetes } if configuredMode == config.ModeEC2 { @@ -122,6 +147,16 @@ func isEKS() eksdetector.IsEKSCache { return eksdetector.IsEKS() } +// isAKS dispatches to azuredetector.IsAKS dynamically (symmetric with isAzureVM). +func isAKS() bool { + return azuredetector.IsAKS() +} + +// isAzureVM reports Azure VM detection; an unreachable IMDS reads as not-Azure. +func isAzureVM() bool { + return azuredetector.IsAzureVM().Value +} + func detectRegion(mode string, credsConfig map[string]string) (region string, regionType string) { region = SDKRegionWithCredsMap(mode, credsConfig) regionType = config.RegionTypeNotFound diff --git a/translator/util/sdkutil_test.go b/translator/util/sdkutil_test.go index 94d4d4f3c65..64fc575c5e5 100644 --- a/translator/util/sdkutil_test.go +++ b/translator/util/sdkutil_test.go @@ -13,45 +13,77 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/util/eksdetector" ) +// restoreDetectionHooks snapshots the overridable detection vars and registers a +// t.Cleanup to restore them, so tests can freely stub them without leaking state. +func restoreDetectionHooks(t *testing.T) { + t.Helper() + origIsEKS, origIsAKS, origIsAzureVM := IsEKS, IsAKS, IsAzureVM + origRunInAws, origEC2, origECS := runInAws, DefaultEC2Region, DefaultECSRegion + t.Cleanup(func() { + IsEKS, IsAKS, IsAzureVM = origIsEKS, origIsAKS, origIsAzureVM + runInAws, DefaultEC2Region, DefaultECSRegion = origRunInAws, origEC2, origECS + }) +} + func TestDetectAgentModeAuto(t *testing.T) { + restoreDetectionHooks(t) + testCases := map[string]struct { runInAws string ec2Region string ecsRegion string + isAKS bool + isAzureVM bool wantMode string }{ - "WithRunInAWS": {runInAws: config.RUN_IN_AWS_TRUE, wantMode: config.ModeEC2}, - "WithEC2Region": {ec2Region: "us-east-1", wantMode: config.ModeEC2}, - "WithECSRegion": {ecsRegion: "us-east-1", wantMode: config.ModeEC2}, - "WithNone": {wantMode: config.ModeOnPrem}, + // AWS detection must be unaffected by the new Azure probe: the Azure + // signals are intentionally true in these cases and must NOT win. + "WithRunInAWS": {runInAws: config.RUN_IN_AWS_TRUE, isAKS: true, isAzureVM: true, wantMode: config.ModeEC2}, + "WithEC2Region": {ec2Region: "us-east-1", isAKS: true, isAzureVM: true, wantMode: config.ModeEC2}, + "WithECSRegion": {ecsRegion: "us-east-1", isAKS: true, isAzureVM: true, wantMode: config.ModeEC2}, + // AKS nodes are Azure VMs, so the host mode resolves to AzureVM. + "AzureHostWhenAKS": {isAKS: true, isAzureVM: false, wantMode: config.ModeAzureVM}, + "AzureVMWhenNoAWS": {isAzureVM: true, wantMode: config.ModeAzureVM}, + "OnPremWhenNoAWSNoAzure": {isAzureVM: false, wantMode: config.ModeOnPrem}, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { runInAws = testCase.runInAws DefaultEC2Region = func() string { return testCase.ec2Region } DefaultECSRegion = func() string { return testCase.ecsRegion } + IsAKS = func() bool { return testCase.isAKS } + IsAzureVM = func() bool { return testCase.isAzureVM } require.Equal(t, testCase.wantMode, DetectAgentMode("auto")) }) } } func TestDetectKubernetesMode(t *testing.T) { + restoreDetectionHooks(t) + testCases := map[string]struct { isEKS bool isEKSErr error + isAKS bool configuredMode string wantKubernetesMode string }{ - "EKS": {isEKS: true, isEKSErr: nil, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeEKS}, - "K8sEC2": {isEKS: false, isEKSErr: nil, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeK8sEC2}, - "K8sOnPrem": {isEKS: false, isEKSErr: nil, configuredMode: config.ModeOnPrem, wantKubernetesMode: config.ModeK8sOnPrem}, - "NotKubernetes": {isEKS: false, isEKSErr: fmt.Errorf("error"), configuredMode: config.ModeEC2, wantKubernetesMode: ""}, + // EKS must keep precedence even if the AKS env signal is also set. + "EKS": {isEKS: true, isEKSErr: nil, isAKS: true, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeEKS}, + "K8sEC2": {isEKS: false, isEKSErr: nil, isAKS: false, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeK8sEC2}, + "K8sOnPrem": {isEKS: false, isEKSErr: nil, isAKS: false, configuredMode: config.ModeOnPrem, wantKubernetesMode: config.ModeK8sOnPrem}, + "NotKubernetes": {isEKS: false, isEKSErr: fmt.Errorf("error"), isAKS: false, configuredMode: config.ModeEC2, wantKubernetesMode: ""}, + // AKS is detected when EKS is absent, even when the EKS probe errored + // (a plain non-EKS cluster / Azure VM with RUN_IN_AKS set). + "AKS": {isEKS: false, isEKSErr: nil, isAKS: true, configuredMode: config.ModeAzureVM, wantKubernetesMode: config.ModeAKS}, + "AKSWhenEKSErr": {isEKS: false, isEKSErr: fmt.Errorf("error"), isAKS: true, configuredMode: config.ModeAzureVM, wantKubernetesMode: config.ModeAKS}, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { IsEKS = func() eksdetector.IsEKSCache { return eksdetector.IsEKSCache{Value: testCase.isEKS, Err: testCase.isEKSErr} } + IsAKS = func() bool { return testCase.isAKS } require.Equal(t, testCase.wantKubernetesMode, DetectKubernetesMode(testCase.configuredMode)) }) } From 70758bdbe1ac1c64024d72358de6fe47403cc21c Mon Sep 17 00:00:00 2001 From: Hyunsoo Kim Date: Thu, 2 Jul 2026 00:26:10 +0000 Subject: [PATCH 2/5] fix(translator): Windows token path, Azure resource_id guard, retry comment - oidctoken output_token_file derives from target platform (fixes Windows CI) - guard EC2 ARN cloud.resource_id transform with cloud.provider == aws - correct azuredetector retry comment (all errors retryable, not cached) --- .../combined_v1_v2_ec2_config.yaml | 6 ++--- .../opentelemetry/dbi_config_linux.yaml | 6 ++--- .../opentelemetry/default_otel_config.yaml | 6 ++--- .../host_metrics_azurevm_config.yaml | 6 ++--- .../opentelemetry/host_metrics_config.yaml | 6 ++--- .../opentelemetry/otlp_otel_config.yaml | 6 ++--- .../prometheus_otel_pipeline_config.yaml | 6 ++--- .../otel/extension/oidctoken/translator.go | 22 +++++++++++++------ .../extension/oidctoken/translator_test.go | 13 ++++++++++- .../transform_identity_host.yaml | 2 +- translator/translate/otel/translate_otel.go | 5 ++++- .../util/azuredetector/azuredetector.go | 5 +++-- 12 files changed, 56 insertions(+), 33 deletions(-) diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml index ba522dcec8b..efc6c8749b0 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml @@ -926,7 +926,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -934,7 +934,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -942,7 +942,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml index 38a0cd965b8..af11563ba5c 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml @@ -729,7 +729,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -737,7 +737,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -745,7 +745,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml index 4df3f62b40a..282c7ad8442 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml @@ -494,7 +494,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -502,7 +502,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -510,7 +510,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml index 30ba4587017..3e7c7b80dad 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml @@ -361,7 +361,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -369,7 +369,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -377,7 +377,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil receivers: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml index 976fe2bdb6d..16b7a70de9d 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml @@ -351,7 +351,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -359,7 +359,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -367,7 +367,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil receivers: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml index 15d9457266d..f874b0b2f0d 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml @@ -464,7 +464,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -472,7 +472,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -480,7 +480,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml index c2c536aa604..173d486d219 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml @@ -340,7 +340,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -348,7 +348,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -356,7 +356,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/prometheus_scope: diff --git a/translator/translate/otel/extension/oidctoken/translator.go b/translator/translate/otel/extension/oidctoken/translator.go index cb350853fe2..c4ed3ec0a3a 100644 --- a/translator/translate/otel/extension/oidctoken/translator.go +++ b/translator/translate/otel/extension/oidctoken/translator.go @@ -4,22 +4,21 @@ package oidctoken import ( - "path/filepath" - "github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidctokenextension" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" - "github.com/aws/amazon-cloudwatch-agent/tool/paths" "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" + "github.com/aws/amazon-cloudwatch-agent/translator/util" ) -// defaultOutputTokenFile is where the extension writes the OIDC token, under the -// agent dir so a sibling auth component (e.g. sigv4auth) can read the same path. -var defaultOutputTokenFile = filepath.Join(paths.AgentDir, "var", "run", "oidc-token") +// linuxOutputTokenFile is where the extension writes the OIDC token on +// Linux/Darwin, under the agent dir so a sibling auth component (e.g. sigv4auth) +// reads the same path. Windows builds the equivalent path under ProgramData. +const linuxOutputTokenFile = "/opt/aws/amazon-cloudwatch-agent/var/run/oidc-token" type translator struct { factory extension.Factory @@ -39,7 +38,7 @@ func (t *translator) ID() component.ID { func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg := t.factory.CreateDefaultConfig().(*oidctokenextension.Config) cfg.Provider = providerForMode(context.CurrentContext().Mode()) - cfg.OutputTokenFile = defaultOutputTokenFile + cfg.OutputTokenFile = outputTokenFile(context.CurrentContext().Os()) return cfg, nil } @@ -50,3 +49,12 @@ func providerForMode(mode string) oidctokenextension.ProviderType { } return oidctokenextension.ProviderAuto } + +// outputTokenFile resolves the token path for the target platform (not the +// runtime OS), mirroring the agent log-file rule so translation is deterministic. +func outputTokenFile(targetPlatform string) string { + if targetPlatform == config.OS_TYPE_WINDOWS { + return util.GetWindowsProgramDataPath() + "\\Amazon\\AmazonCloudWatchAgent\\var\\run\\oidc-token" + } + return linuxOutputTokenFile +} diff --git a/translator/translate/otel/extension/oidctoken/translator_test.go b/translator/translate/otel/extension/oidctoken/translator_test.go index f83600126cc..005f77a5121 100644 --- a/translator/translate/otel/extension/oidctoken/translator_test.go +++ b/translator/translate/otel/extension/oidctoken/translator_test.go @@ -13,12 +13,14 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" + "github.com/aws/amazon-cloudwatch-agent/translator/util" ) func TestTranslator(t *testing.T) { t.Cleanup(context.ResetContext) context.ResetContext() context.CurrentContext().SetMode(config.ModeAzureVM) + context.CurrentContext().SetOs(config.OS_TYPE_LINUX) tt := NewTranslator() assert.Equal(t, "oidctoken", tt.ID().String()) @@ -30,7 +32,7 @@ func TestTranslator(t *testing.T) { cfg, ok := got.(*oidctokenextension.Config) require.True(t, ok) assert.Equal(t, oidctokenextension.ProviderAzure, cfg.Provider) - assert.Equal(t, defaultOutputTokenFile, cfg.OutputTokenFile) + assert.Equal(t, linuxOutputTokenFile, cfg.OutputTokenFile) assert.NoError(t, cfg.Validate()) } @@ -40,3 +42,12 @@ func TestProviderForMode(t *testing.T) { assert.Equal(t, oidctokenextension.ProviderAuto, providerForMode(config.ModeEC2)) assert.Equal(t, oidctokenextension.ProviderAuto, providerForMode(config.ModeOnPrem)) } + +func TestOutputTokenFile(t *testing.T) { + // The path follows the target platform, not the runtime OS, so translation + // is deterministic on any build host. + assert.Equal(t, linuxOutputTokenFile, outputTokenFile(config.OS_TYPE_LINUX)) + assert.Equal(t, linuxOutputTokenFile, outputTokenFile(config.OS_TYPE_DARWIN)) + t.Setenv(util.ProgramData, "C:\\ProgramData") + assert.Equal(t, "C:\\ProgramData\\Amazon\\AmazonCloudWatchAgent\\var\\run\\oidc-token", outputTokenFile(config.OS_TYPE_WINDOWS)) +} diff --git a/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml b/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml index 95393bc76f5..50d744a4802 100644 --- a/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml +++ b/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml @@ -4,7 +4,7 @@ trace_statements: context: resource statements: &identity_statements - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: diff --git a/translator/translate/otel/translate_otel.go b/translator/translate/otel/translate_otel.go index e4bef928a2b..209e62e39f9 100644 --- a/translator/translate/otel/translate_otel.go +++ b/translator/translate/otel/translate_otel.go @@ -115,7 +115,10 @@ func translateInternal(jsonConfig interface{}, os string, validate bool) (*otelc pipelines.Translators.Extensions.Set(server.NewTranslator()) } - // Azure VM has no AWS credentials, so it needs oidctoken for egress auth. + // Azure VM has no AWS credentials, so it needs oidctoken to mint a bearer + // token for egress auth. The extension is emitted here; wiring it as the + // exporter authenticator (replacing sigv4auth) is handled by the Azure + // egress work and is intentionally not part of platform detection. if context.CurrentContext().Mode() == config.ModeAzureVM { pipelines.Translators.Extensions.Set(oidctoken.NewTranslator()) } diff --git a/translator/util/azuredetector/azuredetector.go b/translator/util/azuredetector/azuredetector.go index ef96cf532b2..55560eb6406 100644 --- a/translator/util/azuredetector/azuredetector.go +++ b/translator/util/azuredetector/azuredetector.go @@ -18,7 +18,7 @@ import ( // azureIMDSTimeout bounds one probe attempt so startup can't stall on a non-Azure host. const azureIMDSTimeout = 1 * time.Second -// azureIMDSMaxAttempts retries transient probe errors; a definitive result is not retried. +// azureIMDSMaxAttempts bounds probe retries; a successful response is not retried. const azureIMDSMaxAttempts = 2 // IsAzureVMCache holds the cached result of the Azure VM detection probe. @@ -67,7 +67,8 @@ func isAzureVM() IsAzureVMCache { // defaultProbeAzureIMDS queries the Azure IMDS via the contrib metadata provider, // returning true when a compute document with a VM ID is returned. The provider -// error is transient (IMDS unreachable), so it is retried but never cached. +// does not expose the HTTP status, so any error is treated as retryable; the +// caller does not cache an errored result, letting a boot-time blip re-probe. func defaultProbeAzureIMDS() (bool, error) { var lastErr error for attempt := 0; attempt < azureIMDSMaxAttempts; attempt++ { From 5171ebb2ca85ac320e88eb04b492a0690d320be6 Mon Sep 17 00:00:00 2001 From: Hyunsoo Kim Date: Thu, 2 Jul 2026 15:45:51 +0000 Subject: [PATCH 3/5] fix(translator): wire oidctoken->sigv4 web identity, EC2-scope resource_id guard - sigv4auth sets assume_role.web_identity_token_file to the oidctoken path for AzureVM - guard EC2 cloud.resource_id transform with cloud.platform == aws_ec2 (was cloud.provider == aws, which fired on ECS/EB/Lambda) - move oidc token file to the agent etc dir - add AzureVMWithRoleARN sigv4 test; concise comments --- .../combined_v1_v2_ec2_config.yaml | 6 ++-- .../opentelemetry/dbi_config_linux.yaml | 6 ++-- .../opentelemetry/default_otel_config.yaml | 6 ++-- .../host_metrics_aks_config.json | 5 ++- .../host_metrics_aks_config.yaml | 7 ++-- .../host_metrics_azurevm_config.json | 5 ++- .../host_metrics_azurevm_config.yaml | 13 ++++--- .../opentelemetry/host_metrics_config.yaml | 6 ++-- .../opentelemetry/otlp_otel_config.yaml | 6 ++-- .../prometheus_otel_pipeline_config.yaml | 6 ++-- .../otel/extension/oidctoken/translator.go | 15 ++++---- .../extension/oidctoken/translator_test.go | 6 ++-- .../otel/extension/sigv4auth/translator.go | 5 +++ .../extension/sigv4auth/translator_test.go | 12 +++++++ .../transform_identity_host.yaml | 2 +- translator/translate/otel/translate_otel.go | 8 ++--- .../translate/otel/translate_otel_test.go | 35 +++++++++++++++++++ .../util/azuredetector/azuredetector.go | 8 ++--- 18 files changed, 106 insertions(+), 51 deletions(-) diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml index efc6c8749b0..618b6bd3c7d 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml @@ -926,7 +926,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -934,7 +934,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -942,7 +942,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml index af11563ba5c..9bff360b085 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/dbi_config_linux.yaml @@ -729,7 +729,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -737,7 +737,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -745,7 +745,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml index 282c7ad8442..b03bafc83d2 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config.yaml @@ -494,7 +494,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -502,7 +502,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -510,7 +510,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json index b0f6f9f64d6..8363781c290 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json @@ -1,6 +1,9 @@ { "agent": { - "region": "us-west-2" + "region": "us-west-2", + "credentials": { + "role_arn": "arn:aws:iam::123456789012:role/AzureVMRole" + } }, "opentelemetry": { "collect": { diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml index 51733eebf8e..b2c5260b38d 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml @@ -39,7 +39,7 @@ extensions: mode: AKS region_type: ACJ oidctoken: - output_token_file: /opt/aws/amazon-cloudwatch-agent/var/run/oidc-token + output_token_file: /opt/aws/amazon-cloudwatch-agent/etc/.oidc-token provider: azure server: listen_addr: :4311 @@ -47,7 +47,10 @@ extensions: tls_cert_path: /etc/amazon-cloudwatch-observability-agent-server-cert/server.crt tls_key_path: /etc/amazon-cloudwatch-observability-agent-server-cert/server.key sigv4auth/monitoring: - assume_role: {} + assume_role: + arn: arn:aws:iam::123456789012:role/AzureVMRole + sts_region: us-west-2 + web_identity_token_file: /opt/aws/amazon-cloudwatch-agent/etc/.oidc-token imds_retries: 1 max_retries: 2 num_workers: 8 diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json index b0f6f9f64d6..8363781c290 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json @@ -1,6 +1,9 @@ { "agent": { - "region": "us-west-2" + "region": "us-west-2", + "credentials": { + "role_arn": "arn:aws:iam::123456789012:role/AzureVMRole" + } }, "opentelemetry": { "collect": { diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml index 3e7c7b80dad..386a94470d7 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml @@ -39,10 +39,13 @@ extensions: mode: AZVM region_type: ACJ oidctoken: - output_token_file: /opt/aws/amazon-cloudwatch-agent/var/run/oidc-token + output_token_file: /opt/aws/amazon-cloudwatch-agent/etc/.oidc-token provider: azure sigv4auth/monitoring: - assume_role: {} + assume_role: + arn: arn:aws:iam::123456789012:role/AzureVMRole + sts_region: us-west-2 + web_identity_token_file: /opt/aws/amazon-cloudwatch-agent/etc/.oidc-token imds_retries: 1 max_retries: 2 num_workers: 8 @@ -361,7 +364,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -369,7 +372,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -377,7 +380,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil receivers: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml index 16b7a70de9d..4ef33d3d73a 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.yaml @@ -351,7 +351,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -359,7 +359,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -367,7 +367,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil receivers: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml index f874b0b2f0d..23f95d08bb1 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/otlp_otel_config.yaml @@ -464,7 +464,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -472,7 +472,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -480,7 +480,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/logs_cleanup: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml index 173d486d219..af03ed4c80a 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml @@ -340,7 +340,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: @@ -348,7 +348,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil trace_statements: @@ -356,7 +356,7 @@ processors: error_mode: ignore statements: - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil transform/prometheus_scope: diff --git a/translator/translate/otel/extension/oidctoken/translator.go b/translator/translate/otel/extension/oidctoken/translator.go index c4ed3ec0a3a..e4c3919305b 100644 --- a/translator/translate/otel/extension/oidctoken/translator.go +++ b/translator/translate/otel/extension/oidctoken/translator.go @@ -15,10 +15,8 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/util" ) -// linuxOutputTokenFile is where the extension writes the OIDC token on -// Linux/Darwin, under the agent dir so a sibling auth component (e.g. sigv4auth) -// reads the same path. Windows builds the equivalent path under ProgramData. -const linuxOutputTokenFile = "/opt/aws/amazon-cloudwatch-agent/var/run/oidc-token" +// linuxOutputTokenFile is the token path sigv4auth reads on Linux/Darwin (etc dir). +const linuxOutputTokenFile = "/opt/aws/amazon-cloudwatch-agent/etc/.oidc-token" type translator struct { factory extension.Factory @@ -38,7 +36,7 @@ func (t *translator) ID() component.ID { func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg := t.factory.CreateDefaultConfig().(*oidctokenextension.Config) cfg.Provider = providerForMode(context.CurrentContext().Mode()) - cfg.OutputTokenFile = outputTokenFile(context.CurrentContext().Os()) + cfg.OutputTokenFile = OutputTokenFile(context.CurrentContext().Os()) return cfg, nil } @@ -50,11 +48,10 @@ func providerForMode(mode string) oidctokenextension.ProviderType { return oidctokenextension.ProviderAuto } -// outputTokenFile resolves the token path for the target platform (not the -// runtime OS), mirroring the agent log-file rule so translation is deterministic. -func outputTokenFile(targetPlatform string) string { +// OutputTokenFile returns the token path for the target platform; sigv4auth points web_identity_token_file here. +func OutputTokenFile(targetPlatform string) string { if targetPlatform == config.OS_TYPE_WINDOWS { - return util.GetWindowsProgramDataPath() + "\\Amazon\\AmazonCloudWatchAgent\\var\\run\\oidc-token" + return util.GetWindowsProgramDataPath() + "\\Amazon\\AmazonCloudWatchAgent\\.oidc-token" } return linuxOutputTokenFile } diff --git a/translator/translate/otel/extension/oidctoken/translator_test.go b/translator/translate/otel/extension/oidctoken/translator_test.go index 005f77a5121..c34568dea8f 100644 --- a/translator/translate/otel/extension/oidctoken/translator_test.go +++ b/translator/translate/otel/extension/oidctoken/translator_test.go @@ -46,8 +46,8 @@ func TestProviderForMode(t *testing.T) { func TestOutputTokenFile(t *testing.T) { // The path follows the target platform, not the runtime OS, so translation // is deterministic on any build host. - assert.Equal(t, linuxOutputTokenFile, outputTokenFile(config.OS_TYPE_LINUX)) - assert.Equal(t, linuxOutputTokenFile, outputTokenFile(config.OS_TYPE_DARWIN)) + assert.Equal(t, linuxOutputTokenFile, OutputTokenFile(config.OS_TYPE_LINUX)) + assert.Equal(t, linuxOutputTokenFile, OutputTokenFile(config.OS_TYPE_DARWIN)) t.Setenv(util.ProgramData, "C:\\ProgramData") - assert.Equal(t, "C:\\ProgramData\\Amazon\\AmazonCloudWatchAgent\\var\\run\\oidc-token", outputTokenFile(config.OS_TYPE_WINDOWS)) + assert.Equal(t, "C:\\ProgramData\\Amazon\\AmazonCloudWatchAgent\\.oidc-token", OutputTokenFile(config.OS_TYPE_WINDOWS)) } diff --git a/translator/translate/otel/extension/sigv4auth/translator.go b/translator/translate/otel/extension/sigv4auth/translator.go index 5e4b8fe849a..89cef8b0654 100644 --- a/translator/translate/otel/extension/sigv4auth/translator.go +++ b/translator/translate/otel/extension/sigv4auth/translator.go @@ -16,6 +16,7 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" + "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/oidctoken" ) type translator struct { @@ -55,6 +56,10 @@ func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg.IMDSRetries = retryer.GetDefaultRetryNumber() if agent.Global_Config.Role_arn != "" { cfg.AssumeRole = sigv4authextension.AssumeRole{ARN: agent.Global_Config.Role_arn, STSRegion: agent.Global_Config.Region} + // Azure VM: feed the oidctoken file into AssumeRoleWithWebIdentity for AWS creds. + if context.CurrentContext().Mode() == config.ModeAzureVM { + cfg.AssumeRole.WebIdentityTokenFile = oidctoken.OutputTokenFile(context.CurrentContext().Os()) + } } return cfg, nil diff --git a/translator/translate/otel/extension/sigv4auth/translator_test.go b/translator/translate/otel/extension/sigv4auth/translator_test.go index f11f5a525b3..c320618c889 100644 --- a/translator/translate/otel/extension/sigv4auth/translator_test.go +++ b/translator/translate/otel/extension/sigv4auth/translator_test.go @@ -88,6 +88,17 @@ func TestTranslate(t *testing.T) { STSRegion: "us-east-1", }, }, + "AzureVMWithRoleARN": { + mode: config.ModeAzureVM, + region: "us-west-2", + roleARN: "arn:aws:iam::123456789012:role/azure-role", + wantID: component.MustNewID("sigv4auth"), + wantRole: sigv4authextension.AssumeRole{ + ARN: "arn:aws:iam::123456789012:role/azure-role", + STSRegion: "us-west-2", + WebIdentityTokenFile: "/opt/aws/amazon-cloudwatch-agent/etc/.oidc-token", + }, + }, "OnPremWithProfileAndFileAndRole": { service: "logs", mode: config.ModeOnPrem, @@ -118,6 +129,7 @@ func TestTranslate(t *testing.T) { translateagent.Global_Config.Credentials[translateagent.CredentialsFile_Key] = testCase.credsFile } context.CurrentContext().SetMode(testCase.mode) + context.CurrentContext().SetOs(config.OS_TYPE_LINUX) var tt common.ComponentTranslator if testCase.service != "" { diff --git a/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml b/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml index 50d744a4802..8baf16f31a3 100644 --- a/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml +++ b/translator/translate/otel/processor/transformprocessor/transform_identity_host.yaml @@ -4,7 +4,7 @@ trace_statements: context: resource statements: &identity_statements - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil - - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.provider"] == "aws" + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil metric_statements: diff --git a/translator/translate/otel/translate_otel.go b/translator/translate/otel/translate_otel.go index 209e62e39f9..5805dde97b1 100644 --- a/translator/translate/otel/translate_otel.go +++ b/translator/translate/otel/translate_otel.go @@ -22,6 +22,7 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" + "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/entitystore" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/oidctoken" @@ -115,11 +116,8 @@ func translateInternal(jsonConfig interface{}, os string, validate bool) (*otelc pipelines.Translators.Extensions.Set(server.NewTranslator()) } - // Azure VM has no AWS credentials, so it needs oidctoken to mint a bearer - // token for egress auth. The extension is emitted here; wiring it as the - // exporter authenticator (replacing sigv4auth) is handled by the Azure - // egress work and is intentionally not part of platform detection. - if context.CurrentContext().Mode() == config.ModeAzureVM { + // Azure VM: oidctoken fetches the token sigv4auth exchanges for AWS creds (needs role_arn). + if context.CurrentContext().Mode() == config.ModeAzureVM && agent.Global_Config.Role_arn != "" { pipelines.Translators.Extensions.Set(oidctoken.NewTranslator()) } diff --git a/translator/translate/otel/translate_otel_test.go b/translator/translate/otel/translate_otel_test.go index f122eb12285..04f9d42b80e 100644 --- a/translator/translate/otel/translate_otel_test.go +++ b/translator/translate/otel/translate_otel_test.go @@ -8,11 +8,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/pipeline" "github.com/aws/amazon-cloudwatch-agent/tool/testutil" "github.com/aws/amazon-cloudwatch-agent/translator" + "github.com/aws/amazon-cloudwatch-agent/translator/config" + "github.com/aws/amazon-cloudwatch-agent/translator/context" _ "github.com/aws/amazon-cloudwatch-agent/translator/registerrules" "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" @@ -269,6 +272,38 @@ func TestTranslator(t *testing.T) { } } +func TestOIDCTokenGatedOnRoleARN(t *testing.T) { + t.Setenv("SYSTEM_METRICS_ENABLED", "false") + testutil.SetPrometheusRemoteWriteTestingEnv(t) + translator.SetTargetPlatform("linux") + oidcID := component.MustNewID("oidctoken") + + input := map[string]interface{}{ + "opentelemetry": map[string]interface{}{ + "collect": map[string]interface{}{ + "host_metrics": map[string]interface{}{"metrics_collection_interval": 10}, + }, + }, + } + + for name, roleARN := range map[string]string{"WithRoleARN": "arn:aws:iam::123456789012:role/AzureVMRole", "NoRoleARN": ""} { + t.Run(name, func(t *testing.T) { + context.ResetContext() + t.Cleanup(context.ResetContext) + context.CurrentContext().SetMode(config.ModeAzureVM) + agent.Global_Config.Region = "us-west-2" + agent.Global_Config.Role_arn = roleARN + t.Cleanup(func() { agent.Global_Config.Role_arn = "" }) + + got, err := TranslateWithoutValidation(input, "linux") + require.NoError(t, err) + require.NotNil(t, got) + _, hasOIDC := got.Extensions[oidcID] + assert.Equal(t, roleARN != "", hasOIDC) + }) + } +} + type testTranslator struct { id pipeline.ID version int diff --git a/translator/util/azuredetector/azuredetector.go b/translator/util/azuredetector/azuredetector.go index 55560eb6406..41dcd577bda 100644 --- a/translator/util/azuredetector/azuredetector.go +++ b/translator/util/azuredetector/azuredetector.go @@ -1,8 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT -// Package azuredetector detects whether the CloudWatch agent is running on -// Azure VM or Azure Kubernetes Service. Mirrors the eksdetector package. +// Package azuredetector detects Azure VM / AKS, mirroring the eksdetector package. package azuredetector import ( @@ -65,10 +64,7 @@ func isAzureVM() IsAzureVMCache { return azureVMCache } -// defaultProbeAzureIMDS queries the Azure IMDS via the contrib metadata provider, -// returning true when a compute document with a VM ID is returned. The provider -// does not expose the HTTP status, so any error is treated as retryable; the -// caller does not cache an errored result, letting a boot-time blip re-probe. +// defaultProbeAzureIMDS reports whether the contrib provider returns a compute doc with a VM ID; any error is retryable and uncached. func defaultProbeAzureIMDS() (bool, error) { var lastErr error for attempt := 0; attempt < azureIMDSMaxAttempts; attempt++ { From 9e9745d441dda0907b5f0f4234447bed6c60336f Mon Sep 17 00:00:00 2001 From: Hyunsoo Kim Date: Mon, 6 Jul 2026 18:27:45 +0000 Subject: [PATCH 4/5] refactor(translator): single-source oidctoken path, require role_arn on Azure - Move the OIDC token file path to translator/util (OIDCTokenFilePath), the single source of truth read by both oidctoken (writer) and sigv4auth (reader); removes the sigv4auth->oidctoken package import. - Emit oidctoken whenever Mode()==AzureVM (AKS resolves to AzureVM), and require credentials.role_arn: Azure VM/AKS reaches AWS only via the oidctoken->sigv4 web-identity chain, so a missing role_arn is a hard translate error, not a silent skip that would defer the failure to runtime. - Drop the unreachable ProviderAuto branch; the extension is only emitted for Azure, so the provider is always Azure. --- .../otel/extension/oidctoken/translator.go | 25 ++-------------- .../extension/oidctoken/translator_test.go | 18 +----------- .../otel/extension/sigv4auth/translator.go | 6 ++-- translator/translate/otel/translate_otel.go | 7 +++-- .../translate/otel/translate_otel_test.go | 29 +++++++++++++++---- translator/util/oidctokenpath.go | 19 ++++++++++++ translator/util/oidctokenpath_test.go | 20 +++++++++++++ 7 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 translator/util/oidctokenpath.go create mode 100644 translator/util/oidctokenpath_test.go diff --git a/translator/translate/otel/extension/oidctoken/translator.go b/translator/translate/otel/extension/oidctoken/translator.go index e4c3919305b..0aceb47955c 100644 --- a/translator/translate/otel/extension/oidctoken/translator.go +++ b/translator/translate/otel/extension/oidctoken/translator.go @@ -9,15 +9,11 @@ import ( "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" - "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" "github.com/aws/amazon-cloudwatch-agent/translator/util" ) -// linuxOutputTokenFile is the token path sigv4auth reads on Linux/Darwin (etc dir). -const linuxOutputTokenFile = "/opt/aws/amazon-cloudwatch-agent/etc/.oidc-token" - type translator struct { factory extension.Factory } @@ -35,23 +31,8 @@ func (t *translator) ID() component.ID { func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg := t.factory.CreateDefaultConfig().(*oidctokenextension.Config) - cfg.Provider = providerForMode(context.CurrentContext().Mode()) - cfg.OutputTokenFile = OutputTokenFile(context.CurrentContext().Os()) + // Only emitted for Azure VM/AKS, so the provider is always Azure. + cfg.Provider = oidctokenextension.ProviderAzure + cfg.OutputTokenFile = util.OIDCTokenFilePath(context.CurrentContext().Os()) return cfg, nil } - -// providerForMode maps the detected platform mode to the OIDC token provider. -func providerForMode(mode string) oidctokenextension.ProviderType { - if mode == config.ModeAzureVM { - return oidctokenextension.ProviderAzure - } - return oidctokenextension.ProviderAuto -} - -// OutputTokenFile returns the token path for the target platform; sigv4auth points web_identity_token_file here. -func OutputTokenFile(targetPlatform string) string { - if targetPlatform == config.OS_TYPE_WINDOWS { - return util.GetWindowsProgramDataPath() + "\\Amazon\\AmazonCloudWatchAgent\\.oidc-token" - } - return linuxOutputTokenFile -} diff --git a/translator/translate/otel/extension/oidctoken/translator_test.go b/translator/translate/otel/extension/oidctoken/translator_test.go index c34568dea8f..34daafd7394 100644 --- a/translator/translate/otel/extension/oidctoken/translator_test.go +++ b/translator/translate/otel/extension/oidctoken/translator_test.go @@ -32,22 +32,6 @@ func TestTranslator(t *testing.T) { cfg, ok := got.(*oidctokenextension.Config) require.True(t, ok) assert.Equal(t, oidctokenextension.ProviderAzure, cfg.Provider) - assert.Equal(t, linuxOutputTokenFile, cfg.OutputTokenFile) + assert.Equal(t, util.OIDCTokenFilePath(config.OS_TYPE_LINUX), cfg.OutputTokenFile) assert.NoError(t, cfg.Validate()) } - -func TestProviderForMode(t *testing.T) { - assert.Equal(t, oidctokenextension.ProviderAzure, providerForMode(config.ModeAzureVM)) - // Any non-AzureVM mode defers to the extension's own environment detection. - assert.Equal(t, oidctokenextension.ProviderAuto, providerForMode(config.ModeEC2)) - assert.Equal(t, oidctokenextension.ProviderAuto, providerForMode(config.ModeOnPrem)) -} - -func TestOutputTokenFile(t *testing.T) { - // The path follows the target platform, not the runtime OS, so translation - // is deterministic on any build host. - assert.Equal(t, linuxOutputTokenFile, OutputTokenFile(config.OS_TYPE_LINUX)) - assert.Equal(t, linuxOutputTokenFile, OutputTokenFile(config.OS_TYPE_DARWIN)) - t.Setenv(util.ProgramData, "C:\\ProgramData") - assert.Equal(t, "C:\\ProgramData\\Amazon\\AmazonCloudWatchAgent\\.oidc-token", OutputTokenFile(config.OS_TYPE_WINDOWS)) -} diff --git a/translator/translate/otel/extension/sigv4auth/translator.go b/translator/translate/otel/extension/sigv4auth/translator.go index 89cef8b0654..a4b60eabfa4 100644 --- a/translator/translate/otel/extension/sigv4auth/translator.go +++ b/translator/translate/otel/extension/sigv4auth/translator.go @@ -16,7 +16,7 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" - "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/oidctoken" + "github.com/aws/amazon-cloudwatch-agent/translator/util" ) type translator struct { @@ -56,9 +56,9 @@ func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg.IMDSRetries = retryer.GetDefaultRetryNumber() if agent.Global_Config.Role_arn != "" { cfg.AssumeRole = sigv4authextension.AssumeRole{ARN: agent.Global_Config.Role_arn, STSRegion: agent.Global_Config.Region} - // Azure VM: feed the oidctoken file into AssumeRoleWithWebIdentity for AWS creds. + // Azure VM/AKS: feed the oidctoken file into AssumeRoleWithWebIdentity for AWS creds. if context.CurrentContext().Mode() == config.ModeAzureVM { - cfg.AssumeRole.WebIdentityTokenFile = oidctoken.OutputTokenFile(context.CurrentContext().Os()) + cfg.AssumeRole.WebIdentityTokenFile = util.OIDCTokenFilePath(context.CurrentContext().Os()) } } diff --git a/translator/translate/otel/translate_otel.go b/translator/translate/otel/translate_otel.go index 5805dde97b1..7cc80b0f404 100644 --- a/translator/translate/otel/translate_otel.go +++ b/translator/translate/otel/translate_otel.go @@ -116,8 +116,11 @@ func translateInternal(jsonConfig interface{}, os string, validate bool) (*otelc pipelines.Translators.Extensions.Set(server.NewTranslator()) } - // Azure VM: oidctoken fetches the token sigv4auth exchanges for AWS creds (needs role_arn). - if context.CurrentContext().Mode() == config.ModeAzureVM && agent.Global_Config.Role_arn != "" { + // Azure VM/AKS (AKS resolves to ModeAzureVM) reaches AWS only via oidctoken->sigv4 web identity, which needs role_arn. + if context.CurrentContext().Mode() == config.ModeAzureVM { + if agent.Global_Config.Role_arn == "" { + return nil, errors.New("credentials.role_arn is required on Azure VM/AKS for the oidctoken web-identity credential chain") + } pipelines.Translators.Extensions.Set(oidctoken.NewTranslator()) } diff --git a/translator/translate/otel/translate_otel_test.go b/translator/translate/otel/translate_otel_test.go index 04f9d42b80e..40b708c9497 100644 --- a/translator/translate/otel/translate_otel_test.go +++ b/translator/translate/otel/translate_otel_test.go @@ -272,7 +272,7 @@ func TestTranslator(t *testing.T) { } } -func TestOIDCTokenGatedOnRoleARN(t *testing.T) { +func TestOIDCTokenGatedOnAzureMode(t *testing.T) { t.Setenv("SYSTEM_METRICS_ENABLED", "false") testutil.SetPrometheusRemoteWriteTestingEnv(t) translator.SetTargetPlatform("linux") @@ -286,20 +286,37 @@ func TestOIDCTokenGatedOnRoleARN(t *testing.T) { }, } - for name, roleARN := range map[string]string{"WithRoleARN": "arn:aws:iam::123456789012:role/AzureVMRole", "NoRoleARN": ""} { + testCases := map[string]struct { + mode string + roleARN string + wantOIDC bool + wantErr string + }{ + // Azure VM/AKS reaches AWS only via the oidctoken web-identity chain, so role_arn is mandatory. + "AzureVMWithRoleARN": {mode: config.ModeAzureVM, roleARN: "arn:aws:iam::123456789012:role/AzureVMRole", wantOIDC: true}, + "AzureVMMissingRoleARN": {mode: config.ModeAzureVM, roleARN: "", wantErr: "role_arn is required"}, + "EC2NoOIDC": {mode: config.ModeEC2, roleARN: "", wantOIDC: false}, + } + + for name, tc := range testCases { t.Run(name, func(t *testing.T) { context.ResetContext() t.Cleanup(context.ResetContext) - context.CurrentContext().SetMode(config.ModeAzureVM) + context.CurrentContext().SetMode(tc.mode) agent.Global_Config.Region = "us-west-2" - agent.Global_Config.Role_arn = roleARN - t.Cleanup(func() { agent.Global_Config.Role_arn = "" }) + agent.Global_Config.Role_arn = tc.roleARN + t.Cleanup(func() { agent.Global_Config.Region = ""; agent.Global_Config.Role_arn = "" }) got, err := TranslateWithoutValidation(input, "linux") + if tc.wantErr != "" { + require.Error(t, err) + assert.ErrorContains(t, err, tc.wantErr) + return + } require.NoError(t, err) require.NotNil(t, got) _, hasOIDC := got.Extensions[oidcID] - assert.Equal(t, roleARN != "", hasOIDC) + assert.Equal(t, tc.wantOIDC, hasOIDC) }) } } diff --git a/translator/util/oidctokenpath.go b/translator/util/oidctokenpath.go new file mode 100644 index 00000000000..0b02d5dd196 --- /dev/null +++ b/translator/util/oidctokenpath.go @@ -0,0 +1,19 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package util + +import ( + "github.com/aws/amazon-cloudwatch-agent/translator/config" +) + +// linuxOIDCTokenFile is the OIDC token path shared by oidctoken (writer) and sigv4auth (reader) on Linux/Darwin (etc dir). +const linuxOIDCTokenFile = "/opt/aws/amazon-cloudwatch-agent/etc/.oidc-token" + +// OIDCTokenFilePath returns the OIDC token file path for the target platform; single source of truth for oidctoken and sigv4auth. +func OIDCTokenFilePath(targetPlatform string) string { + if targetPlatform == config.OS_TYPE_WINDOWS { + return GetWindowsProgramDataPath() + "\\Amazon\\AmazonCloudWatchAgent\\.oidc-token" + } + return linuxOIDCTokenFile +} diff --git a/translator/util/oidctokenpath_test.go b/translator/util/oidctokenpath_test.go new file mode 100644 index 00000000000..f327c3e1a6f --- /dev/null +++ b/translator/util/oidctokenpath_test.go @@ -0,0 +1,20 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/aws/amazon-cloudwatch-agent/translator/config" +) + +func TestOIDCTokenFilePath(t *testing.T) { + // The path follows the target platform, not the runtime OS, so translation is deterministic on any build host. + assert.Equal(t, linuxOIDCTokenFile, OIDCTokenFilePath(config.OS_TYPE_LINUX)) + assert.Equal(t, linuxOIDCTokenFile, OIDCTokenFilePath(config.OS_TYPE_DARWIN)) + t.Setenv(ProgramData, "C:\\ProgramData") + assert.Equal(t, "C:\\ProgramData\\Amazon\\AmazonCloudWatchAgent\\.oidc-token", OIDCTokenFilePath(config.OS_TYPE_WINDOWS)) +} From 1b3214693245c19a05060758e58e1e6abe51fb58 Mon Sep 17 00:00:00 2001 From: Hyunsoo Kim Date: Mon, 6 Jul 2026 23:04:25 +0000 Subject: [PATCH 5/5] =?UTF-8?q?refactor(translator):=20address=20review=20?= =?UTF-8?q?=E2=80=94=20fix=20gosec=20lint,=20simplify=20azuredetector,=20g?= =?UTF-8?q?ate=20oidctoken=20on=20sigv4auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix CI lint (gosec G101): move the OIDC token path into tool/paths (OIDCTokenFile + LinuxOIDCTokenPath, literal so it stays linux-valued when targeting linux on a Windows build host); util.OIDCTokenFilePath now sources from there. - azuredetector: drop the IsAzureVMCache struct/unused Err and the mutex in favor of atomic.Pointer[bool]; remove the dead probeAzureIMDS indirection var and the isAKS wrapper (IsAKS = envconfig.IsRunningInAKS directly); use for range on the retry loop. - sdkutil: collapse the isAKS/isAzureVM wrappers now that azuredetector returns bool. - Emit oidctoken only when a sigv4auth extension is present (co-located with the consumer, across all sigv4auth pipelines), instead of on ModeAzureVM alone. --- cfg/envconfig/envconfig.go | 4 +- tool/paths/paths.go | 2 + tool/paths/paths_unix.go | 1 + tool/paths/paths_windows.go | 1 + translator/config/mode.go | 3 +- translator/context/context_test.go | 8 +- .../host_metrics_aks_config.json | 5 +- .../host_metrics_aks_config.yaml | 27 +- .../host_metrics_azurevm_config.yaml | 18 +- ...st_metrics_azurevm_sharedcreds_config.conf | 15 + ...st_metrics_azurevm_sharedcreds_config.json | 15 + ...st_metrics_azurevm_sharedcreds_config.yaml | 446 ++++++++++++++++++ translator/tocwconfig/tocwconfig_test.go | 19 - translator/tocwconfig/tocwconfig_unix_test.go | 28 ++ translator/translate/agent/ruleCommonCreds.go | 14 + .../otel/extension/oidctoken/translator.go | 6 +- .../extension/oidctoken/translator_test.go | 5 +- .../otel/extension/sigv4auth/translator.go | 13 +- .../extension/sigv4auth/translator_test.go | 3 +- .../resourcedetection/translator_test.go | 3 +- translator/translate/otel/translate_otel.go | 18 +- .../util/azuredetector/azuredetector.go | 57 +-- .../util/azuredetector/azuredetector_test.go | 70 +-- translator/util/oidctokenpath.go | 19 - translator/util/oidctokenpath_test.go | 20 - translator/util/sdkutil.go | 30 +- translator/util/sdkutil_test.go | 17 +- 27 files changed, 625 insertions(+), 242 deletions(-) create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.conf create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json create mode 100644 translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.yaml delete mode 100644 translator/util/oidctokenpath.go delete mode 100644 translator/util/oidctokenpath_test.go diff --git a/cfg/envconfig/envconfig.go b/cfg/envconfig/envconfig.go index 6883226a3fa..0cb5ace687e 100644 --- a/cfg/envconfig/envconfig.go +++ b/cfg/envconfig/envconfig.go @@ -90,9 +90,7 @@ func IsRunningInROSA() bool { return os.Getenv(RunInROSA) == TrueValue } -// IsRunningInAKS returns true when the RUN_IN_AKS environment variable is set to -// the expected true value. The CWA Helm chart for Azure Kubernetes Service (AKS) -// sets this variable so the translator can detect AKS without a metadata probe. +// IsRunningInAKS reports whether RUN_IN_AKS is set (by the AKS Helm chart) so AKS is detected without a metadata probe. func IsRunningInAKS() bool { return os.Getenv(RunInAKS) == TrueValue } diff --git a/tool/paths/paths.go b/tool/paths/paths.go index 1c77da707ca..6702dd3254e 100644 --- a/tool/paths/paths.go +++ b/tool/paths/paths.go @@ -11,6 +11,7 @@ const ( ENV = "env-config.json" AGENT_LOG_FILE = "amazon-cloudwatch-agent.log" JMXJarName = "opentelemetry-jmx-metrics.jar" + OIDCToken = ".oidc-token" ) var ( @@ -24,4 +25,5 @@ var ( TranslatorBinaryPath string AgentBinaryPath string JMXJarPath string + OIDCTokenPath string ) diff --git a/tool/paths/paths_unix.go b/tool/paths/paths_unix.go index 801e4239d74..09a79f02670 100644 --- a/tool/paths/paths_unix.go +++ b/tool/paths/paths_unix.go @@ -31,4 +31,5 @@ func init() { TranslatorBinaryPath = filepath.Join(AgentDir, "bin", TranslatorBinaryName) AgentBinaryPath = filepath.Join(AgentDir, "bin", AgentBinaryName) JMXJarPath = filepath.Join(AgentDir, "bin", JMXJarName) + OIDCTokenPath = filepath.Join(AgentDir, "etc", OIDCToken) } diff --git a/tool/paths/paths_windows.go b/tool/paths/paths_windows.go index 0f3305405ff..c35b53c677c 100644 --- a/tool/paths/paths_windows.go +++ b/tool/paths/paths_windows.go @@ -53,4 +53,5 @@ func init() { TranslatorBinaryPath = filepath.Join(AgentRootDir, TranslatorBinaryName) AgentBinaryPath = filepath.Join(AgentRootDir, AgentBinaryName) JMXJarPath = filepath.Join(AgentRootDir, JMXJarName) + OIDCTokenPath = filepath.Join(AgentConfigDir, OIDCToken) } diff --git a/translator/config/mode.go b/translator/config/mode.go index dca470e6b97..1191380495d 100644 --- a/translator/config/mode.go +++ b/translator/config/mode.go @@ -20,8 +20,7 @@ const ( ModeK8sOnPrem = "K8sOnPrem" ) -// Azure platform modes. ModeAzureVM is a host-level mode analogous to -// ModeEC2; ModeAKS is a Kubernetes-level mode analogous to ModeEKS. +// Azure platform modes: ModeAzureVM is host-level (like ModeEC2), ModeAKS is Kubernetes-level (like ModeEKS). const ( ModeAzureVM = "AzureVM" ModeAKS = "AKS" diff --git a/translator/context/context_test.go b/translator/context/context_test.go index 50dd7b63b0b..65189ff19cd 100644 --- a/translator/context/context_test.go +++ b/translator/context/context_test.go @@ -76,18 +76,14 @@ func TestSetKubernetesMode_UnknownClears(t *testing.T) { ctx.SetKubernetesMode(config.ModeAKS) ctx.SetKubernetesMode("not-a-real-mode") assert.Equal(t, "", ctx.KubernetesMode()) - // shortMode is intentionally NOT cleared by the default branch: the normal - // non-Kubernetes path is SetMode(host) -> SetKubernetesMode("") and must keep - // the host shortMode set by SetMode. Here no host mode was set, so it retains - // the last valid kubernetes shortMode (ShortModeAKS). + // The default branch intentionally keeps shortMode (SetMode sets it first); with no host mode set it retains the last k8s shortMode. assert.Equal(t, config.ShortModeAKS, ctx.ShortMode()) } func TestSetKubernetesMode_EmptyPreservesHostShortMode(t *testing.T) { ResetContext() ctx := CurrentContext() - // Mimic the real startup ordering: host mode first, then the empty - // (non-Kubernetes) kubernetes mode. The host shortMode must survive. + // Real startup ordering: host mode first, then empty (non-Kubernetes) mode; the host shortMode must survive. ctx.SetMode(config.ModeAzureVM) ctx.SetKubernetesMode("") assert.Equal(t, "", ctx.KubernetesMode()) diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json index 8363781c290..b0f6f9f64d6 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json @@ -1,9 +1,6 @@ { "agent": { - "region": "us-west-2", - "credentials": { - "role_arn": "arn:aws:iam::123456789012:role/AzureVMRole" - } + "region": "us-west-2" }, "opentelemetry": { "collect": { diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml index b2c5260b38d..54d9d687eb5 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.yaml @@ -38,19 +38,13 @@ extensions: usage_flags: mode: AKS region_type: ACJ - oidctoken: - output_token_file: /opt/aws/amazon-cloudwatch-agent/etc/.oidc-token - provider: azure server: listen_addr: :4311 tls_ca_path: /etc/amazon-cloudwatch-observability-agent-client-cert/tls-ca.crt tls_cert_path: /etc/amazon-cloudwatch-observability-agent-server-cert/server.crt tls_key_path: /etc/amazon-cloudwatch-observability-agent-server-cert/server.key sigv4auth/monitoring: - assume_role: - arn: arn:aws:iam::123456789012:role/AzureVMRole - sts_region: us-west-2 - web_identity_token_file: /opt/aws/amazon-cloudwatch-agent/etc/.oidc-token + assume_role: {} imds_retries: 1 max_retries: 2 num_workers: 8 @@ -402,24 +396,14 @@ processors: transform/host_metrics_scope: error_mode: ignore flatten_data: false - log_statements: - - context: scope - error_mode: ignore - statements: - - set(attributes["cloudwatch.source"], "cloudwatch-agent") - - set(attributes["cloudwatch.solution"], "otel-host-metrics") + log_statements: [] metric_statements: - context: scope error_mode: ignore statements: - - set(attributes["cloudwatch.source"], "cloudwatch-agent") - - set(attributes["cloudwatch.solution"], "otel-host-metrics") - trace_statements: - - context: scope - error_mode: ignore - statements: - - set(attributes["cloudwatch.source"], "cloudwatch-agent") - - set(attributes["cloudwatch.solution"], "otel-host-metrics") + - set(scope.attributes["cloudwatch.source"], "cloudwatch-agent") + - set(scope.attributes["cloudwatch.solution"], "otel-host-metrics") + trace_statements: [] transform/identity: error_mode: ignore flatten_data: false @@ -587,7 +571,6 @@ service: - sigv4auth/monitoring - agenthealth/opentelemetry_metrics - server - - oidctoken pipelines: metrics/host_metrics: exporters: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml index 386a94470d7..52052e83be8 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.yaml @@ -338,24 +338,14 @@ processors: transform/host_metrics_scope: error_mode: ignore flatten_data: false - log_statements: - - context: scope - error_mode: ignore - statements: - - set(attributes["cloudwatch.source"], "cloudwatch-agent") - - set(attributes["cloudwatch.solution"], "otel-host-metrics") + log_statements: [] metric_statements: - context: scope error_mode: ignore statements: - - set(attributes["cloudwatch.source"], "cloudwatch-agent") - - set(attributes["cloudwatch.solution"], "otel-host-metrics") - trace_statements: - - context: scope - error_mode: ignore - statements: - - set(attributes["cloudwatch.source"], "cloudwatch-agent") - - set(attributes["cloudwatch.solution"], "otel-host-metrics") + - set(scope.attributes["cloudwatch.source"], "cloudwatch-agent") + - set(scope.attributes["cloudwatch.solution"], "otel-host-metrics") + trace_statements: [] transform/identity: error_mode: ignore flatten_data: false diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.conf b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.conf new file mode 100644 index 00000000000..9d2168ce61a --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.conf @@ -0,0 +1,15 @@ +[agent] + collection_jitter = "0s" + debug = false + flush_interval = "1s" + flush_jitter = "0s" + hostname = "" + interval = "60s" + logfile = "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log" + logtarget = "lumberjack" + metric_batch_size = 1000 + metric_buffer_limit = 10000 + omit_hostname = false + precision = "" + quiet = false + round_interval = false diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json new file mode 100644 index 00000000000..8363781c290 --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json @@ -0,0 +1,15 @@ +{ + "agent": { + "region": "us-west-2", + "credentials": { + "role_arn": "arn:aws:iam::123456789012:role/AzureVMRole" + } + }, + "opentelemetry": { + "collect": { + "host_metrics": { + "metrics_collection_interval": 10 + } + } + } +} diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.yaml new file mode 100644 index 00000000000..2c69633b70f --- /dev/null +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.yaml @@ -0,0 +1,446 @@ +connectors: + forward/opentelemetry: {} +exporters: + otlphttp/metrics: + auth: + authenticator: agenthealth/opentelemetry_metrics + compression: gzip + encoding: proto + idle_conn_timeout: 1m30s + logs_endpoint: "" + max_idle_conns: 100 + metrics_endpoint: https://monitoring.us-west-2.amazonaws.com/v1/metrics + retry_on_failure: + enabled: true + initial_interval: 5s + max_elapsed_time: 5m0s + max_interval: 30s + multiplier: 1.5 + randomization_factor: 0.5 + sending_queue: + block_on_overflow: false + blocking: false + enabled: true + num_consumers: 10 + queue_size: 1000 + sizer: {} + wait_for_result: false + timeout: 30s + traces_endpoint: "" + write_buffer_size: 524288 +extensions: + agenthealth/opentelemetry_metrics: + additional_auth: sigv4auth/monitoring + is_usage_data_enabled: true + stats: + operations: + - '*' + usage_flags: + mode: AZVM + region_type: ACJ + sigv4auth/monitoring: + assume_role: + arn: arn:aws:iam::123456789012:role/AzureVMRole + sts_region: us-west-2 + imds_retries: 2 + max_retries: 2 + num_workers: 8 + profile: default + region: us-west-2 + request_timeout_seconds: 30 + service: monitoring + shared_credentials_file: + - /root/.aws/credentials +processors: + batch/opentelemetry_metrics: + metadata_cardinality_limit: 1000 + send_batch_max_size: 1000 + send_batch_size: 1000 + timeout: 30s + resourcedetection/opentelemetry: + aks: + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + k8s.cluster.name: + enabled: false + azure: + resource_attributes: + azure.resourcegroup.name: + enabled: true + azure.vm.name: + enabled: true + azure.vm.scaleset.name: + enabled: true + azure.vm.size: + enabled: true + cloud.account.id: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + tags: [] + consul: + address: "" + datacenter: "" + namespace: "" + resource_attributes: + cloud.region: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + token_file: "" + detectors: + - env + - azure + docker: + resource_attributes: + host.name: + enabled: true + os.type: + enabled: true + ec2: + fail_on_missing_metadata: false + max_attempts: 3 + max_backoff: 20s + resource_attributes: + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + host.id: + enabled: true + host.image.id: + enabled: true + host.name: + enabled: true + host.type: + enabled: true + tags: [] + ecs: + resource_attributes: + aws.ecs.cluster.arn: + enabled: true + aws.ecs.launchtype: + enabled: true + aws.ecs.task.arn: + enabled: true + aws.ecs.task.family: + enabled: true + aws.ecs.task.id: + enabled: true + aws.ecs.task.revision: + enabled: true + aws.log.group.arns: + enabled: true + aws.log.group.names: + enabled: true + aws.log.stream.arns: + enabled: true + aws.log.stream.names: + enabled: true + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + eks: + resource_attributes: + cloud.account.id: + enabled: false + cloud.platform: + enabled: true + cloud.provider: + enabled: true + k8s.cluster.name: + enabled: false + elasticbeanstalk: + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + deployment.environment: + enabled: true + service.instance.id: + enabled: true + service.version: + enabled: true + gcp: + resource_attributes: + cloud.account.id: + enabled: true + cloud.availability_zone: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + faas.id: + enabled: true + faas.instance: + enabled: true + faas.name: + enabled: true + faas.version: + enabled: true + gcp.cloud_run.job.execution: + enabled: true + gcp.cloud_run.job.task_index: + enabled: true + gcp.gce.instance.hostname: + enabled: false + gcp.gce.instance.name: + enabled: false + gcp.gce.instance_group_manager.name: + enabled: true + gcp.gce.instance_group_manager.region: + enabled: true + gcp.gce.instance_group_manager.zone: + enabled: true + host.id: + enabled: true + host.name: + enabled: true + host.type: + enabled: true + k8s.cluster.name: + enabled: true + heroku: + resource_attributes: + cloud.provider: + enabled: true + heroku.app.id: + enabled: true + heroku.dyno.id: + enabled: true + heroku.release.commit: + enabled: true + heroku.release.creation_timestamp: + enabled: true + service.instance.id: + enabled: true + service.name: + enabled: true + service.version: + enabled: true + idle_conn_timeout: 1m30s + k8snode: + auth_type: serviceAccount + context: "" + kube_config_path: "" + node_from_env_var: "" + resource_attributes: + k8s.node.name: + enabled: true + k8s.node.uid: + enabled: true + kubeadm: + auth_type: serviceAccount + context: "" + kube_config_path: "" + resource_attributes: + k8s.cluster.name: + enabled: true + k8s.cluster.uid: + enabled: true + lambda: + resource_attributes: + aws.log.group.names: + enabled: true + aws.log.stream.names: + enabled: true + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + faas.instance: + enabled: true + faas.max_memory: + enabled: true + faas.name: + enabled: true + faas.version: + enabled: true + max_idle_conns: 100 + openshift: + address: "" + resource_attributes: + cloud.platform: + enabled: true + cloud.provider: + enabled: true + cloud.region: + enabled: true + k8s.cluster.name: + enabled: true + tls: {} + token: "" + override: true + system: + resource_attributes: + host.arch: + enabled: false + host.cpu.cache.l2.size: + enabled: false + host.cpu.family: + enabled: false + host.cpu.model.id: + enabled: false + host.cpu.model.name: + enabled: false + host.cpu.stepping: + enabled: false + host.cpu.vendor.id: + enabled: false + host.id: + enabled: false + host.ip: + enabled: false + host.mac: + enabled: false + host.name: + enabled: true + os.description: + enabled: false + os.type: + enabled: true + os.version: + enabled: false + timeout: 2s + transform/host_metrics_scope: + error_mode: ignore + flatten_data: false + log_statements: [] + metric_statements: + - context: scope + error_mode: ignore + statements: + - set(scope.attributes["cloudwatch.source"], "cloudwatch-agent") + - set(scope.attributes["cloudwatch.solution"], "otel-host-metrics") + trace_statements: [] + transform/identity: + error_mode: ignore + flatten_data: false + log_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil + metric_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil + trace_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["service.name"], "unknown_service") where resource.attributes["service.name"] == nil + - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:ec2", resource.attributes["cloud.region"], resource.attributes["cloud.account.id"], Concat(["instance", resource.attributes["host.id"]], "/")], ":")) where resource.attributes["cloud.resource_id"] == nil and resource.attributes["host.id"] != nil and resource.attributes["cloud.platform"] == "aws_ec2" + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], resource.attributes["ec2.tag.aws:autoscaling:groupName"]], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["ec2.tag.aws:autoscaling:groupName"] != nil + - set(resource.attributes["deployment.environment.name"], Concat([resource.attributes["cloud.platform"], "default"], ":")) where resource.attributes["deployment.environment.name"] == nil and resource.attributes["cloud.platform"] != nil +receivers: + hostmetrics/opentelemetry: + collection_interval: 10s + initial_delay: 1s + scrapers: + cpu: + metrics: + system.cpu.frequency: + enabled: true + system.cpu.logical.count: + enabled: true + system.cpu.physical.count: + enabled: true + system.cpu.utilization: + enabled: true + disk: null + filesystem: + metrics: + system.filesystem.utilization: + enabled: true + load: null + memory: + metrics: + system.linux.memory.available: + enabled: true + system.linux.memory.dirty: + enabled: true + system.memory.limit: + enabled: true + system.memory.page_size: + enabled: true + system.memory.utilization: + enabled: true + network: null + processes: null + timeout: 0s +service: + extensions: + - sigv4auth/monitoring + - agenthealth/opentelemetry_metrics + pipelines: + metrics/host_metrics: + exporters: + - forward/opentelemetry + processors: + - transform/host_metrics_scope + receivers: + - hostmetrics/opentelemetry + metrics/opentelemetry: + exporters: + - otlphttp/metrics + processors: + - resourcedetection/opentelemetry + - transform/identity + - batch/opentelemetry_metrics + receivers: + - forward/opentelemetry + telemetry: + logs: + encoding: console + level: info + output_paths: + - /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log + sampling: + enabled: true + initial: 2 + thereafter: 500 + tick: 10s + metrics: + level: None + traces: + level: None diff --git a/translator/tocwconfig/tocwconfig_test.go b/translator/tocwconfig/tocwconfig_test.go index 358d766c5f3..e81c57cd1cf 100644 --- a/translator/tocwconfig/tocwconfig_test.go +++ b/translator/tocwconfig/tocwconfig_test.go @@ -132,25 +132,6 @@ func TestAppSignalsAndEKSConfig(t *testing.T) { checkTranslation(t, "appsignals_and_eks_config", "windows", expectedEnvVars, "") } -func TestAzureVMHostMetricsConfig(t *testing.T) { - resetContext(t) - context.CurrentContext().SetMode(config.ModeAzureVM) - - checkTranslation(t, "opentelemetry/host_metrics_azurevm_config", "linux", nil, "") -} - -func TestAKSHostMetricsConfig(t *testing.T) { - resetContext(t) - context.CurrentContext().SetRunInContainer(true) - t.Setenv(envconfig.RunInAKS, envconfig.TrueValue) - // AKS nodes are Azure VMs, so DetectAgentMode resolves the host mode to - // AzureVM; mirror that here so the host-level config is generated correctly. - context.CurrentContext().SetMode(config.ModeAzureVM) - context.CurrentContext().SetKubernetesMode(config.ModeAKS) - - checkTranslation(t, "opentelemetry/host_metrics_aks_config", "linux", nil, "") -} - func TestAppSignalsFallbackAndEKSConfig(t *testing.T) { resetContext(t) context.CurrentContext().SetRunInContainer(true) diff --git a/translator/tocwconfig/tocwconfig_unix_test.go b/translator/tocwconfig/tocwconfig_unix_test.go index ee5d7022c37..eb0adfb322c 100644 --- a/translator/tocwconfig/tocwconfig_unix_test.go +++ b/translator/tocwconfig/tocwconfig_unix_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" + "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" "github.com/aws/amazon-cloudwatch-agent/internal/mapstructure" "github.com/aws/amazon-cloudwatch-agent/tool/testutil" "github.com/aws/amazon-cloudwatch-agent/translator" @@ -183,3 +184,30 @@ func TestCombinedV1V2EKSConfig(t *testing.T) { }) assert.Empty(t, cmp.Diff(expected, actual, opt)) } + +func TestAzureVMHostMetricsConfig(t *testing.T) { + resetContext(t) + context.CurrentContext().SetMode(config.ModeAzureVM) + + checkTranslation(t, "opentelemetry/host_metrics_azurevm_config", "linux", nil, "") +} + +func TestAzureVMHostMetricsSharedCredsConfig(t *testing.T) { + resetContext(t) + context.CurrentContext().SetMode(config.ModeAzureVM) + // common-config.toml creds: sigv4auth assumes the role directly, so no oidctoken/web_identity path. + readCommonConfig(t, "./sampleConfig/commonConfig/withCredentials.toml") + + checkTranslation(t, "opentelemetry/host_metrics_azurevm_sharedcreds_config", "linux", nil, "") +} + +func TestAKSHostMetricsConfig(t *testing.T) { + resetContext(t) + context.CurrentContext().SetRunInContainer(true) + t.Setenv(envconfig.RunInAKS, envconfig.TrueValue) + // AKS nodes are Azure VMs, so DetectAgentMode resolves host mode to AzureVM; mirror that here. + context.CurrentContext().SetMode(config.ModeAzureVM) + context.CurrentContext().SetKubernetesMode(config.ModeAKS) + + checkTranslation(t, "opentelemetry/host_metrics_aks_config", "linux", nil, "") +} diff --git a/translator/translate/agent/ruleCommonCreds.go b/translator/translate/agent/ruleCommonCreds.go index c2ca9bb3e2d..e66938012ab 100644 --- a/translator/translate/agent/ruleCommonCreds.go +++ b/translator/translate/agent/ruleCommonCreds.go @@ -5,6 +5,7 @@ package agent import ( "github.com/aws/amazon-cloudwatch-agent/cfg/commonconfig" + "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/util" ) @@ -54,6 +55,19 @@ func (c *CommonCreds) ApplyRule(input interface{}) (returnKey string, returnVal return } +// HasSharedCredentials reports whether common-config.toml supplied a profile or credentials file. +func HasSharedCredentials() bool { + _, hasProfile := Global_Config.Credentials[Profile_Key] + _, hasFile := Global_Config.Credentials[CredentialsFile_Key] + return hasProfile || hasFile +} + +// IsAzureWebIdentity reports whether the oidctoken IMDS web-identity chain applies: plain Azure VM (not AKS, which uses the chart's projected SA token) with no common-config credentials. +func IsAzureWebIdentity() bool { + ctx := context.CurrentContext() + return ctx.Mode() == config.ModeAzureVM && ctx.KubernetesMode() != config.ModeAKS && !HasSharedCredentials() +} + func init() { c := new(CommonCreds) RegisterRule(CommonCredentialsSectionKey, c) diff --git a/translator/translate/otel/extension/oidctoken/translator.go b/translator/translate/otel/extension/oidctoken/translator.go index 0aceb47955c..3483e3c505e 100644 --- a/translator/translate/otel/extension/oidctoken/translator.go +++ b/translator/translate/otel/extension/oidctoken/translator.go @@ -9,9 +9,8 @@ import ( "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" - "github.com/aws/amazon-cloudwatch-agent/translator/context" + "github.com/aws/amazon-cloudwatch-agent/tool/paths" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" - "github.com/aws/amazon-cloudwatch-agent/translator/util" ) type translator struct { @@ -20,7 +19,6 @@ type translator struct { var _ common.ComponentTranslator = (*translator)(nil) -// NewTranslator returns the oidctoken extension translator (Azure provider). func NewTranslator() common.ComponentTranslator { return &translator{factory: oidctokenextension.NewFactory()} } @@ -33,6 +31,6 @@ func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg := t.factory.CreateDefaultConfig().(*oidctokenextension.Config) // Only emitted for Azure VM/AKS, so the provider is always Azure. cfg.Provider = oidctokenextension.ProviderAzure - cfg.OutputTokenFile = util.OIDCTokenFilePath(context.CurrentContext().Os()) + cfg.OutputTokenFile = paths.OIDCTokenPath return cfg, nil } diff --git a/translator/translate/otel/extension/oidctoken/translator_test.go b/translator/translate/otel/extension/oidctoken/translator_test.go index 34daafd7394..cb17eb9a277 100644 --- a/translator/translate/otel/extension/oidctoken/translator_test.go +++ b/translator/translate/otel/extension/oidctoken/translator_test.go @@ -11,16 +11,15 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/confmap" + "github.com/aws/amazon-cloudwatch-agent/tool/paths" "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" - "github.com/aws/amazon-cloudwatch-agent/translator/util" ) func TestTranslator(t *testing.T) { t.Cleanup(context.ResetContext) context.ResetContext() context.CurrentContext().SetMode(config.ModeAzureVM) - context.CurrentContext().SetOs(config.OS_TYPE_LINUX) tt := NewTranslator() assert.Equal(t, "oidctoken", tt.ID().String()) @@ -32,6 +31,6 @@ func TestTranslator(t *testing.T) { cfg, ok := got.(*oidctokenextension.Config) require.True(t, ok) assert.Equal(t, oidctokenextension.ProviderAzure, cfg.Provider) - assert.Equal(t, util.OIDCTokenFilePath(config.OS_TYPE_LINUX), cfg.OutputTokenFile) + assert.Equal(t, paths.OIDCTokenPath, cfg.OutputTokenFile) assert.NoError(t, cfg.Validate()) } diff --git a/translator/translate/otel/extension/sigv4auth/translator.go b/translator/translate/otel/extension/sigv4auth/translator.go index a4b60eabfa4..8dc577a7060 100644 --- a/translator/translate/otel/extension/sigv4auth/translator.go +++ b/translator/translate/otel/extension/sigv4auth/translator.go @@ -12,11 +12,11 @@ import ( "go.opentelemetry.io/collector/extension" "github.com/aws/amazon-cloudwatch-agent/internal/retryer" + "github.com/aws/amazon-cloudwatch-agent/tool/paths" "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" - "github.com/aws/amazon-cloudwatch-agent/translator/util" ) type translator struct { @@ -26,6 +26,11 @@ type translator struct { var _ common.ComponentTranslator = (*translator)(nil) +// Type is the sigv4auth extension's component type. +func Type() component.Type { + return sigv4authextension.NewFactory().Type() +} + func NewTranslator() common.ComponentTranslator { return &translator{factory: sigv4authextension.NewFactory()} } @@ -56,9 +61,9 @@ func (t *translator) Translate(_ *confmap.Conf) (component.Config, error) { cfg.IMDSRetries = retryer.GetDefaultRetryNumber() if agent.Global_Config.Role_arn != "" { cfg.AssumeRole = sigv4authextension.AssumeRole{ARN: agent.Global_Config.Role_arn, STSRegion: agent.Global_Config.Region} - // Azure VM/AKS: feed the oidctoken file into AssumeRoleWithWebIdentity for AWS creds. - if context.CurrentContext().Mode() == config.ModeAzureVM { - cfg.AssumeRole.WebIdentityTokenFile = util.OIDCTokenFilePath(context.CurrentContext().Os()) + // Feed the oidctoken file into AssumeRoleWithWebIdentity for the Azure VM web-identity chain. + if agent.IsAzureWebIdentity() { + cfg.AssumeRole.WebIdentityTokenFile = paths.OIDCTokenPath } } diff --git a/translator/translate/otel/extension/sigv4auth/translator_test.go b/translator/translate/otel/extension/sigv4auth/translator_test.go index c320618c889..6b6e0a858c9 100644 --- a/translator/translate/otel/extension/sigv4auth/translator_test.go +++ b/translator/translate/otel/extension/sigv4auth/translator_test.go @@ -12,6 +12,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" + "github.com/aws/amazon-cloudwatch-agent/tool/paths" "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" translateagent "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" @@ -96,7 +97,7 @@ func TestTranslate(t *testing.T) { wantRole: sigv4authextension.AssumeRole{ ARN: "arn:aws:iam::123456789012:role/azure-role", STSRegion: "us-west-2", - WebIdentityTokenFile: "/opt/aws/amazon-cloudwatch-agent/etc/.oidc-token", + WebIdentityTokenFile: paths.OIDCTokenPath, }, }, "OnPremWithProfileAndFileAndRole": { diff --git a/translator/translate/otel/processor/resourcedetection/translator_test.go b/translator/translate/otel/processor/resourcedetection/translator_test.go index 2b6ca771244..5fe5be4caa0 100644 --- a/translator/translate/otel/processor/resourcedetection/translator_test.go +++ b/translator/translate/otel/processor/resourcedetection/translator_test.go @@ -158,8 +158,7 @@ func TestTranslate(t *testing.T) { factory := resourcedetectionprocessor.NewFactory() for name, testCase := range testCases { t.Run(name, func(t *testing.T) { - // Reset so kubernetesMode/mode does not bleed across cases (map - // iteration order is non-deterministic). + // Reset so kubernetesMode/mode does not bleed across cases (map iteration is non-deterministic). context.ResetContext() if testCase.kubernetesMode != "" { context.CurrentContext().SetKubernetesMode(testCase.kubernetesMode) diff --git a/translator/translate/otel/translate_otel.go b/translator/translate/otel/translate_otel.go index 7cc80b0f404..82a57a9b0e6 100644 --- a/translator/translate/otel/translate_otel.go +++ b/translator/translate/otel/translate_otel.go @@ -20,13 +20,13 @@ import ( "go.uber.org/multierr" "go.uber.org/zap/zapcore" - "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/translate/agent" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/entitystore" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/oidctoken" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/server" + "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/sigv4auth" pipelinetranslator "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline/applicationsignals" "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline/containerinsights" @@ -116,10 +116,10 @@ func translateInternal(jsonConfig interface{}, os string, validate bool) (*otelc pipelines.Translators.Extensions.Set(server.NewTranslator()) } - // Azure VM/AKS (AKS resolves to ModeAzureVM) reaches AWS only via oidctoken->sigv4 web identity, which needs role_arn. - if context.CurrentContext().Mode() == config.ModeAzureVM { + // Emit oidctoken only when a sigv4auth extension consumes it; that web-identity path needs role_arn. + if agent.IsAzureWebIdentity() && hasSigv4auth(pipelines.Translators.Extensions) { if agent.Global_Config.Role_arn == "" { - return nil, errors.New("credentials.role_arn is required on Azure VM/AKS for the oidctoken web-identity credential chain") + return nil, errors.New("credentials.role_arn is required on Azure VM for the oidctoken web-identity credential chain") } pipelines.Translators.Extensions.Set(oidctoken.NewTranslator()) } @@ -151,6 +151,16 @@ func translateInternal(jsonConfig interface{}, os string, validate bool) (*otelc return cfg, nil } +// hasSigv4auth reports whether a sigv4auth extension is present, i.e. some pipeline will consume the oidctoken file. +func hasSigv4auth(extensions common.ComponentTranslatorMap) bool { + for _, id := range extensions.Keys() { + if id.Type() == sigv4auth.Type() { + return true + } + } + return false +} + // parseAgentLogLevel returns the logging level from the JSON config, or the // default value. func parseAgentLogLevel(conf *confmap.Conf) zapcore.Level { diff --git a/translator/util/azuredetector/azuredetector.go b/translator/util/azuredetector/azuredetector.go index 41dcd577bda..65054757ed1 100644 --- a/translator/util/azuredetector/azuredetector.go +++ b/translator/util/azuredetector/azuredetector.go @@ -6,7 +6,7 @@ package azuredetector import ( "context" - "sync" + "sync/atomic" "time" azuremeta "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/metadataproviders/azure" @@ -20,54 +20,39 @@ const azureIMDSTimeout = 1 * time.Second // azureIMDSMaxAttempts bounds probe retries; a successful response is not retried. const azureIMDSMaxAttempts = 2 -// IsAzureVMCache holds the cached result of the Azure VM detection probe. -type IsAzureVMCache struct { - Value bool - Err error -} - var ( - // metadataProvider is the Azure IMDS provider; overridable in tests. metadataProvider = azuremeta.NewProvider() - probeAzureIMDS = defaultProbeAzureIMDS - isRunningInAKS = envconfig.IsRunningInAKS - // Public detection entry points; overridable in tests (mirrors eksdetector.IsEKS). + // IsAzureVM probes Azure IMDS (cached) and IsAKS reads RUN_IN_AKS; both are vars so they can be stubbed. IsAzureVM = isAzureVM - IsAKS = isAKS + IsAKS = envconfig.IsRunningInAKS - // Azure VM detection cache; mutex (not sync.Once) so tests can reset it safely. - azureVMMu sync.Mutex - azureVMResolved bool - azureVMCache IsAzureVMCache + // azureVMCache holds a definitive probe result; a transient error stays uncached so boot can re-probe. + azureVMCache atomic.Pointer[bool] ) -// isAKS reports whether RUN_IN_AKS is set (by the AKS Helm chart); no I/O. -func isAKS() bool { - return isRunningInAKS() +// isAzureVM reports whether the host is an Azure VM (cached); an unreachable probe reports false. +func isAzureVM() bool { + ok, _ := detectAzureVM() + return ok } -// isAzureVM probes Azure IMDS (cached). An unreachable probe reports false. -func isAzureVM() IsAzureVMCache { - azureVMMu.Lock() - defer azureVMMu.Unlock() - if !azureVMResolved { - value, err := probeAzureIMDS() - result := IsAzureVMCache{Value: value, Err: err} - // Cache only a definitive answer so a boot-time transient error can re-probe. - if err == nil { - azureVMCache = result - azureVMResolved = true - } - return result +// detectAzureVM probes Azure IMDS once, caching only a definitive result; returns the error for tests. +func detectAzureVM() (bool, error) { + if c := azureVMCache.Load(); c != nil { + return *c, nil + } + value, err := probeAzureIMDS() + if err == nil { + azureVMCache.CompareAndSwap(nil, &value) } - return azureVMCache + return value, err } -// defaultProbeAzureIMDS reports whether the contrib provider returns a compute doc with a VM ID; any error is retryable and uncached. -func defaultProbeAzureIMDS() (bool, error) { +// probeAzureIMDS reports whether the contrib provider returns a compute doc with a VM ID; any error is retryable and uncached. +func probeAzureIMDS() (bool, error) { var lastErr error - for attempt := 0; attempt < azureIMDSMaxAttempts; attempt++ { + for range azureIMDSMaxAttempts { ctx, cancel := context.WithTimeout(context.Background(), azureIMDSTimeout) compute, err := metadataProvider.Metadata(ctx) cancel() diff --git a/translator/util/azuredetector/azuredetector_test.go b/translator/util/azuredetector/azuredetector_test.go index 8b3ae380953..76276605cc1 100644 --- a/translator/util/azuredetector/azuredetector_test.go +++ b/translator/util/azuredetector/azuredetector_test.go @@ -26,50 +26,27 @@ func (f *fakeProvider) Metadata(context.Context) (*azuremeta.ComputeMetadata, er return f.compute, f.err } -// setProvider swaps metadataProvider for the test and resets the cache, -// restoring both on cleanup. +// setProvider swaps metadataProvider and resets the cache for the test, restoring both on cleanup. func setProvider(t *testing.T, p azuremeta.Provider) { t.Helper() orig := metadataProvider metadataProvider = p - resetCacheForTesting() + azureVMCache.Store(nil) t.Cleanup(func() { metadataProvider = orig - resetCacheForTesting() + azureVMCache.Store(nil) }) } -// resetCacheForTesting clears the Azure VM detection cache under its lock. -func resetCacheForTesting() { - azureVMMu.Lock() - defer azureVMMu.Unlock() - azureVMCache = IsAzureVMCache{} - azureVMResolved = false -} - // ---- AKS (env-var) detection ---- -func TestIsAKS_EnvSet(t *testing.T) { - orig := isRunningInAKS - t.Cleanup(func() { isRunningInAKS = orig }) - isRunningInAKS = func() bool { return true } - assert.True(t, isAKS()) -} - -func TestIsAKS_EnvUnset(t *testing.T) { - orig := isRunningInAKS - t.Cleanup(func() { isRunningInAKS = orig }) - isRunningInAKS = func() bool { return false } - assert.False(t, isAKS()) -} - func TestIsAKS_ReadsRunInAKSEnvVar(t *testing.T) { - // Exercise the real envconfig-backed check to confirm the wiring to RUN_IN_AKS. + // IsAKS is the real envconfig-backed check; confirm the wiring to RUN_IN_AKS. t.Setenv(envconfig.RunInAKS, envconfig.TrueValue) - assert.True(t, isAKS()) + assert.True(t, IsAKS()) t.Setenv(envconfig.RunInAKS, "") - assert.False(t, isAKS()) + assert.False(t, IsAKS()) } // ---- Azure VM (IMDS) detection ---- @@ -77,9 +54,9 @@ func TestIsAKS_ReadsRunInAKSEnvVar(t *testing.T) { func TestIsAzureVM_Detected(t *testing.T) { setProvider(t, &fakeProvider{compute: &azuremeta.ComputeMetadata{VMID: "vm-123", Name: "vm1"}}) - result := isAzureVM() - assert.True(t, result.Value) - assert.NoError(t, result.Err) + ok, err := detectAzureVM() + assert.True(t, ok) + assert.NoError(t, err) } func TestIsAzureVM_NotAzure(t *testing.T) { @@ -91,9 +68,9 @@ func TestIsAzureVM_NotAzure(t *testing.T) { for name, compute := range cases { t.Run(name, func(t *testing.T) { setProvider(t, &fakeProvider{compute: compute}) - result := isAzureVM() - assert.False(t, result.Value) - assert.NoError(t, result.Err) + ok, err := detectAzureVM() + assert.False(t, ok) + assert.NoError(t, err) }) } } @@ -103,26 +80,25 @@ func TestIsAzureVM_Unreachable(t *testing.T) { p := &fakeProvider{err: errors.New("dial tcp 169.254.169.254: connect: no route to host")} setProvider(t, p) - result := isAzureVM() - assert.False(t, result.Value) - assert.Error(t, result.Err) + ok, err := detectAzureVM() + assert.False(t, ok) + assert.Error(t, err) assert.Equal(t, azureIMDSMaxAttempts, p.calls, "a transient error should be retried") } func TestIsAzureVM_TransientErrorNotCached(t *testing.T) { - // A probe that exhausts retries with a transient error must NOT cache a - // negative: a later call (IMDS now up) must be able to re-probe. + // A transient error that exhausts retries must NOT be cached; a later call (IMDS now up) must re-probe. p := &transientThenOKProvider{failFirst: azureIMDSMaxAttempts} setProvider(t, p) - first := isAzureVM() - assert.False(t, first.Value) - assert.Error(t, first.Err) + first, err := detectAzureVM() + assert.False(t, first) + assert.Error(t, err) assert.Equal(t, azureIMDSMaxAttempts, p.calls, "first call should exhaust all retry attempts") - second := isAzureVM() - assert.True(t, second.Value, "a transient failure must not be cached; re-probe should succeed") - assert.NoError(t, second.Err) + second, err := detectAzureVM() + assert.True(t, second, "a transient failure must not be cached; re-probe should succeed") + assert.NoError(t, err) assert.Equal(t, azureIMDSMaxAttempts+1, p.calls, "second call should require exactly one successful probe") } @@ -132,7 +108,7 @@ func TestIsAzureVM_ResultIsCached(t *testing.T) { first := isAzureVM() second := isAzureVM() - assert.True(t, first.Value) + assert.True(t, first) assert.Equal(t, first, second) assert.Equal(t, 1, p.calls, "IMDS should be queried at most once due to caching") } diff --git a/translator/util/oidctokenpath.go b/translator/util/oidctokenpath.go deleted file mode 100644 index 0b02d5dd196..00000000000 --- a/translator/util/oidctokenpath.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package util - -import ( - "github.com/aws/amazon-cloudwatch-agent/translator/config" -) - -// linuxOIDCTokenFile is the OIDC token path shared by oidctoken (writer) and sigv4auth (reader) on Linux/Darwin (etc dir). -const linuxOIDCTokenFile = "/opt/aws/amazon-cloudwatch-agent/etc/.oidc-token" - -// OIDCTokenFilePath returns the OIDC token file path for the target platform; single source of truth for oidctoken and sigv4auth. -func OIDCTokenFilePath(targetPlatform string) string { - if targetPlatform == config.OS_TYPE_WINDOWS { - return GetWindowsProgramDataPath() + "\\Amazon\\AmazonCloudWatchAgent\\.oidc-token" - } - return linuxOIDCTokenFile -} diff --git a/translator/util/oidctokenpath_test.go b/translator/util/oidctokenpath_test.go deleted file mode 100644 index f327c3e1a6f..00000000000 --- a/translator/util/oidctokenpath_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package util - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aws/amazon-cloudwatch-agent/translator/config" -) - -func TestOIDCTokenFilePath(t *testing.T) { - // The path follows the target platform, not the runtime OS, so translation is deterministic on any build host. - assert.Equal(t, linuxOIDCTokenFile, OIDCTokenFilePath(config.OS_TYPE_LINUX)) - assert.Equal(t, linuxOIDCTokenFile, OIDCTokenFilePath(config.OS_TYPE_DARWIN)) - t.Setenv(ProgramData, "C:\\ProgramData") - assert.Equal(t, "C:\\ProgramData\\Amazon\\AmazonCloudWatchAgent\\.oidc-token", OIDCTokenFilePath(config.OS_TYPE_WINDOWS)) -} diff --git a/translator/util/sdkutil.go b/translator/util/sdkutil.go index 94bd873d11c..2e8f2ee8feb 100644 --- a/translator/util/sdkutil.go +++ b/translator/util/sdkutil.go @@ -32,8 +32,8 @@ var DefaultECSRegion = defaultECSRegion var IsEKS = isEKS // IsAKS/IsAzureVM are overridable detection hooks; tests override these vars. -var IsAKS = isAKS -var IsAzureVM = isAzureVM +var IsAKS = azuredetector.IsAKS +var IsAzureVM = azuredetector.IsAzureVM var runInAws = os.Getenv(config.RUN_IN_AWS) var runWithIrsa = os.Getenv(config.RUN_WITH_IRSA) @@ -62,9 +62,7 @@ func DetectAgentMode(configuredMode string) string { return config.ModeEC2 } - // Azure is checked only after all AWS signals, so it can't override them. - // AKS nodes are Azure VMs; RUN_IN_AKS is checked before the IMDS probe since - // an AKS pod may not reach IMDS from its container. + // Azure is checked only after all AWS signals; RUN_IN_AKS is checked before the IMDS probe since an AKS pod may not reach IMDS. if IsAKS() { fmt.Println("I! Detected from ENV instance is Azure (AKS)") return config.ModeAzureVM @@ -81,18 +79,16 @@ func DetectAgentMode(configuredMode string) string { } func DetectKubernetesMode(configuredMode string) string { - isEKS := IsEKS() + // RUN_IN_AKS is an explicit env signal (no I/O), so short-circuit before the EKS in-cluster probe. + if IsAKS() { + return config.ModeAKS + } - // EKS wins over AKS when confirmed. + isEKS := IsEKS() if isEKS.Err == nil && isEKS.Value { return config.ModeEKS } - // AKS is an explicit env signal, so it applies even if the EKS probe errored. - if IsAKS() { - return config.ModeAKS - } - if isEKS.Err != nil { return "" // not kubernetes } @@ -147,16 +143,6 @@ func isEKS() eksdetector.IsEKSCache { return eksdetector.IsEKS() } -// isAKS dispatches to azuredetector.IsAKS dynamically (symmetric with isAzureVM). -func isAKS() bool { - return azuredetector.IsAKS() -} - -// isAzureVM reports Azure VM detection; an unreachable IMDS reads as not-Azure. -func isAzureVM() bool { - return azuredetector.IsAzureVM().Value -} - func detectRegion(mode string, credsConfig map[string]string) (region string, regionType string) { region = SDKRegionWithCredsMap(mode, credsConfig) regionType = config.RegionTypeNotFound diff --git a/translator/util/sdkutil_test.go b/translator/util/sdkutil_test.go index 64fc575c5e5..f45dca5b557 100644 --- a/translator/util/sdkutil_test.go +++ b/translator/util/sdkutil_test.go @@ -13,8 +13,7 @@ import ( "github.com/aws/amazon-cloudwatch-agent/translator/util/eksdetector" ) -// restoreDetectionHooks snapshots the overridable detection vars and registers a -// t.Cleanup to restore them, so tests can freely stub them without leaking state. +// restoreDetectionHooks snapshots the overridable detection vars and restores them on cleanup so stubs don't leak. func restoreDetectionHooks(t *testing.T) { t.Helper() origIsEKS, origIsAKS, origIsAzureVM := IsEKS, IsAKS, IsAzureVM @@ -36,8 +35,7 @@ func TestDetectAgentModeAuto(t *testing.T) { isAzureVM bool wantMode string }{ - // AWS detection must be unaffected by the new Azure probe: the Azure - // signals are intentionally true in these cases and must NOT win. + // AWS detection must win: the Azure signals are intentionally true here and must NOT override it. "WithRunInAWS": {runInAws: config.RUN_IN_AWS_TRUE, isAKS: true, isAzureVM: true, wantMode: config.ModeEC2}, "WithEC2Region": {ec2Region: "us-east-1", isAKS: true, isAzureVM: true, wantMode: config.ModeEC2}, "WithECSRegion": {ecsRegion: "us-east-1", isAKS: true, isAzureVM: true, wantMode: config.ModeEC2}, @@ -68,15 +66,14 @@ func TestDetectKubernetesMode(t *testing.T) { configuredMode string wantKubernetesMode string }{ - // EKS must keep precedence even if the AKS env signal is also set. - "EKS": {isEKS: true, isEKSErr: nil, isAKS: true, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeEKS}, + "EKS": {isEKS: true, isEKSErr: nil, isAKS: false, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeEKS}, "K8sEC2": {isEKS: false, isEKSErr: nil, isAKS: false, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeK8sEC2}, "K8sOnPrem": {isEKS: false, isEKSErr: nil, isAKS: false, configuredMode: config.ModeOnPrem, wantKubernetesMode: config.ModeK8sOnPrem}, "NotKubernetes": {isEKS: false, isEKSErr: fmt.Errorf("error"), isAKS: false, configuredMode: config.ModeEC2, wantKubernetesMode: ""}, - // AKS is detected when EKS is absent, even when the EKS probe errored - // (a plain non-EKS cluster / Azure VM with RUN_IN_AKS set). - "AKS": {isEKS: false, isEKSErr: nil, isAKS: true, configuredMode: config.ModeAzureVM, wantKubernetesMode: config.ModeAKS}, - "AKSWhenEKSErr": {isEKS: false, isEKSErr: fmt.Errorf("error"), isAKS: true, configuredMode: config.ModeAzureVM, wantKubernetesMode: config.ModeAKS}, + // RUN_IN_AKS short-circuits to AKS without the EKS probe, regardless of what EKS detection would report. + "AKS": {isEKS: false, isEKSErr: nil, isAKS: true, configuredMode: config.ModeAzureVM, wantKubernetesMode: config.ModeAKS}, + "AKSWhenEKSErr": {isEKS: false, isEKSErr: fmt.Errorf("error"), isAKS: true, configuredMode: config.ModeAzureVM, wantKubernetesMode: config.ModeAKS}, + "AKSWinsOverEKS": {isEKS: true, isEKSErr: nil, isAKS: true, configuredMode: config.ModeEC2, wantKubernetesMode: config.ModeAKS}, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) {