diff --git a/translator/config/sampleSchema/opentelemetry/invalidOpenTelemetryPrometheus.json b/translator/config/sampleSchema/opentelemetry/invalidOpenTelemetryPrometheus.json index 0026d88f4d..b7aacb3a60 100644 --- a/translator/config/sampleSchema/opentelemetry/invalidOpenTelemetryPrometheus.json +++ b/translator/config/sampleSchema/opentelemetry/invalidOpenTelemetryPrometheus.json @@ -3,9 +3,9 @@ "region": "us-west-2" }, "opentelemetry": { + "cluster_name": "my-cluster", "collect": { "prometheus": { - "cluster_name": "my-cluster" } } } diff --git a/translator/config/sampleSchema/opentelemetry/validCombinedV1V2Config.json b/translator/config/sampleSchema/opentelemetry/validCombinedV1V2Config.json index 4cbf987324..27e7c56cb9 100644 --- a/translator/config/sampleSchema/opentelemetry/validCombinedV1V2Config.json +++ b/translator/config/sampleSchema/opentelemetry/validCombinedV1V2Config.json @@ -34,12 +34,12 @@ } }, "opentelemetry": { + "cluster_name": "TestCluster", "collect": { "host_metrics": { - "metrics_collection_interval": 60 + "collection_interval": 60 }, "container_insights": { - "cluster_name": "TestCluster", "collection_interval": 30 }, "database_insights": { diff --git a/translator/config/schema.json b/translator/config/schema.json index 648b6fdb03..26758e2e24 100644 --- a/translator/config/schema.json +++ b/translator/config/schema.json @@ -1806,6 +1806,10 @@ "opentelemetryDefinition": { "type": "object", "properties": { + "cluster_name": { + "description": "The name of the EKS cluster. Applied to all components under opentelemetry.", + "type": "string" + }, "collect": { "type": "object", "properties": { @@ -1840,7 +1844,7 @@ "host_metrics": { "type": "object", "properties": { - "metrics_collection_interval": { + "collection_interval": { "description": "How often the metrics defined will be collected", "$ref": "#/definitions/timeIntervalDefinition" } @@ -1850,10 +1854,6 @@ "container_insights": { "type": "object", "properties": { - "cluster_name": { - "description": "The name of the EKS cluster", - "type": "string" - }, "collection_interval": { "description": "How often metrics are collected", "$ref": "#/definitions/timeIntervalDefinition" @@ -1874,7 +1874,6 @@ "additionalProperties": false } }, - "required": ["cluster_name"], "additionalProperties": false }, "prometheus": { @@ -1883,10 +1882,6 @@ "config_path": { "description": "Path to the Prometheus scrape configuration file", "type": "string" - }, - "cluster_name": { - "description": "The name of the EKS cluster", - "type": "string" } }, "required": ["config_path"], diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.json index d63dc6fcb9..69a53e5653 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.json @@ -125,9 +125,10 @@ "endpoint_override": "https://x-ray-endpoint.us-west-2.amazonaws.com" }, "opentelemetry": { + "cluster_name": "TestCluster", "collect": { "host_metrics": { - "metrics_collection_interval": 60 + "collection_interval": 60 }, "database_insights": { "postgresql": [ 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 618b6bd3c7..1b80862a2a 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_ec2_config.yaml @@ -1017,6 +1017,26 @@ processors: - set(scope.attributes["cloudwatch.source"], "cloudwatch-agent") - set(scope.attributes["cloudwatch.solution"], "otel-prometheus") trace_statements: [] + transform/set_cluster_name: + error_mode: ignore + flatten_data: false + log_statements: [] + metric_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["k8s.cluster.name"], "TestCluster") + trace_statements: [] + transform/set_cluster_name_logs: + error_mode: ignore + flatten_data: false + log_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["k8s.cluster.name"], "TestCluster") + metric_statements: [] + trace_statements: [] receivers: awsxray: endpoint: 0.0.0.0:2000 @@ -1531,6 +1551,7 @@ service: - forward/opentelemetry processors: - transform/otlp_scope + - transform/set_cluster_name_logs - transform/otlp_log_source receivers: - otlp/grpc_127_0_0_1_4327 @@ -1553,18 +1574,19 @@ service: - ec2tagger - awsentity/resource receivers: - - telegraf_cpu - - telegraf_processes + - telegraf_procstat/1917393364 + - telegraf_swap - telegraf_mem + - telegraf_cpu - telegraf_netstat - telegraf_disk - - telegraf_procstat/1917393364 - - telegraf_swap + - telegraf_processes metrics/host_metrics: exporters: - forward/opentelemetry processors: - transform/host_metrics_scope + - transform/set_cluster_name receivers: - hostmetrics/opentelemetry metrics/hostCustomMetrics: @@ -1584,8 +1606,8 @@ service: - ec2tagger - awsentity/resource receivers: - - telegraf_net - telegraf_diskio + - telegraf_net metrics/opentelemetry: exporters: - otlphttp/metrics @@ -1600,6 +1622,7 @@ service: - forward/opentelemetry processors: - transform/prometheus_scope + - transform/set_cluster_name receivers: - prometheus/opentelemetry metrics/otlp: @@ -1607,6 +1630,7 @@ service: - forward/opentelemetry processors: - transform/otlp_scope + - transform/set_cluster_name receivers: - otlp/grpc_127_0_0_1_4327 - otlp/http_127_0_0_1_4328 diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.json index 0dd61edc92..86ab573d2e 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.json @@ -125,12 +125,12 @@ "endpoint_override": "https://x-ray-endpoint.us-west-2.amazonaws.com" }, "opentelemetry": { + "cluster_name": "TestCluster", "collect": { "host_metrics": { - "metrics_collection_interval": 60 + "collection_interval": 60 }, "container_insights": { - "cluster_name": "TestCluster", "collection_interval": 30 }, "database_insights": { @@ -147,8 +147,7 @@ ] }, "prometheus": { - "config_path": "./sampleConfig/opentelemetry/prometheus_otel_scrape_config.yaml", - "cluster_name": "TestCluster" + "config_path": "./sampleConfig/opentelemetry/prometheus_otel_scrape_config.yaml" }, "otlp": { "grpc_endpoint": "127.0.0.1:4327", diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.yaml index 84971c6654..a127d5db0f 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/combined_v1_v2_eks_config.yaml @@ -1557,6 +1557,16 @@ processors: statements: - set(resource.attributes["k8s.cluster.name"], "TestCluster") trace_statements: [] + transform/set_cluster_name_logs: + error_mode: ignore + flatten_data: false + log_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["k8s.cluster.name"], "TestCluster") + metric_statements: [] + trace_statements: [] receivers: awsefareceiver/cw_k8s_ci_v0: collection_interval: 30s @@ -2228,6 +2238,7 @@ service: - forward/opentelemetry processors: - transform/otlp_scope + - transform/set_cluster_name_logs - transform/otlp_log_source receivers: - otlp/grpc_127_0_0_1_4327 @@ -2418,18 +2429,19 @@ service: - ec2tagger - awsentity/resource receivers: + - telegraf_disk + - telegraf_netstat + - telegraf_cpu + - telegraf_procstat/1917393364 - telegraf_swap - telegraf_mem - telegraf_processes - - telegraf_netstat - - telegraf_disk - - telegraf_procstat/1917393364 - - telegraf_cpu metrics/host_metrics: exporters: - forward/opentelemetry processors: - transform/host_metrics_scope + - transform/set_cluster_name receivers: - hostmetrics/opentelemetry metrics/hostCustomMetrics: @@ -2439,8 +2451,8 @@ service: - ec2tagger - awsentity/service/telegraf receivers: - - telegraf_statsd - telegraf_socket_listener + - telegraf_statsd metrics/hostDeltaMetrics: exporters: - awscloudwatch @@ -2449,8 +2461,8 @@ service: - ec2tagger - awsentity/resource receivers: - - telegraf_net - telegraf_diskio + - telegraf_net metrics/opentelemetry: exporters: - otlphttp/metrics @@ -2474,6 +2486,7 @@ service: - forward/opentelemetry processors: - transform/otlp_scope + - transform/set_cluster_name receivers: - otlp/grpc_127_0_0_1_4327 - otlp/http_127_0_0_1_4328 diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/container_insights_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/container_insights_config.json index 97b4f4f11b..82b3b7ec01 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/container_insights_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/container_insights_config.json @@ -3,9 +3,9 @@ "region": "us-east-1" }, "opentelemetry": { + "cluster_name": "TestCluster", "collect": { "container_insights": { - "cluster_name": "TestCluster", "collection_interval": 30 } } diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json index b0f6f9f64d..5a64ceecf2 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_aks_config.json @@ -5,7 +5,7 @@ "opentelemetry": { "collect": { "host_metrics": { - "metrics_collection_interval": 10 + "collection_interval": 10 } } } diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json index 8363781c29..efba5f6a92 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_config.json @@ -8,7 +8,7 @@ "opentelemetry": { "collect": { "host_metrics": { - "metrics_collection_interval": 10 + "collection_interval": 10 } } } diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json index 8363781c29..efba5f6a92 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_azurevm_sharedcreds_config.json @@ -8,7 +8,7 @@ "opentelemetry": { "collect": { "host_metrics": { - "metrics_collection_interval": 10 + "collection_interval": 10 } } } diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.json index b0f6f9f64d..5a64ceecf2 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/host_metrics_config.json @@ -5,7 +5,7 @@ "opentelemetry": { "collect": { "host_metrics": { - "metrics_collection_interval": 10 + "collection_interval": 10 } } } diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.json index ae4cf31ace..5d937d9076 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.json @@ -3,6 +3,7 @@ "region": "us-east-1" }, "opentelemetry": { + "cluster_name": "TestCluster", "collect": { "prometheus": { "config_path": "./sampleConfig/opentelemetry/prometheus_otel_scrape_config.yaml" diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml index af03ed4c80..473fefbe6d 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_config.yaml @@ -370,6 +370,16 @@ processors: - set(scope.attributes["cloudwatch.source"], "cloudwatch-agent") - set(scope.attributes["cloudwatch.solution"], "otel-prometheus") trace_statements: [] + transform/set_cluster_name: + error_mode: ignore + flatten_data: false + log_statements: [] + metric_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["k8s.cluster.name"], "TestCluster") + trace_statements: [] receivers: prometheus/opentelemetry: config: @@ -425,6 +435,7 @@ service: - forward/opentelemetry processors: - transform/prometheus_scope + - transform/set_cluster_name receivers: - prometheus/opentelemetry telemetry: diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.json b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.json index ae4cf31ace..5d937d9076 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.json +++ b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.json @@ -3,6 +3,7 @@ "region": "us-east-1" }, "opentelemetry": { + "cluster_name": "TestCluster", "collect": { "prometheus": { "config_path": "./sampleConfig/opentelemetry/prometheus_otel_scrape_config.yaml" diff --git a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.yaml b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.yaml index dd01e4d13d..ad845d2db4 100644 --- a/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.yaml +++ b/translator/tocwconfig/sampleConfig/opentelemetry/prometheus_otel_pipeline_eks_config.yaml @@ -532,6 +532,16 @@ processors: - set(scope.attributes["cloudwatch.source"], "cloudwatch-agent") - set(scope.attributes["cloudwatch.solution"], "otel-prometheus") trace_statements: [] + transform/set_cluster_name: + error_mode: ignore + flatten_data: false + log_statements: [] + metric_statements: + - context: resource + error_mode: ignore + statements: + - set(resource.attributes["k8s.cluster.name"], "TestCluster") + trace_statements: [] receivers: prometheus/opentelemetry: config: @@ -589,6 +599,7 @@ service: - forward/opentelemetry processors: - transform/prometheus_scope + - transform/set_cluster_name receivers: - prometheus/opentelemetry telemetry: diff --git a/translator/tocwconfig/tocwconfig_unix_test.go b/translator/tocwconfig/tocwconfig_unix_test.go index eb0adfb322..0d2d7c394a 100644 --- a/translator/tocwconfig/tocwconfig_unix_test.go +++ b/translator/tocwconfig/tocwconfig_unix_test.go @@ -177,6 +177,7 @@ func TestCombinedV1V2EKSConfig(t *testing.T) { yamlConfig, err := mapstructure.Marshal(cfg) require.NoError(t, err) yamlStr := toyamlconfig.ToYamlConfig(yamlConfig) + // assert.NoError(t, os.WriteFile("./sampleConfig/opentelemetry/combined_v1_v2_eks_config.yaml", []byte(yamlStr), 0644)) // useful for regenerating YAML require.NoError(t, yaml.Unmarshal([]byte(yamlStr), &actual)) opt := cmpopts.SortSlices(func(x, y interface{}) bool { diff --git a/translator/translate/otel/common/common.go b/translator/translate/otel/common/common.go index c9752142d6..bc2d9088b8 100644 --- a/translator/translate/otel/common/common.go +++ b/translator/translate/otel/common/common.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "reflect" + "regexp" "strconv" "strings" "time" @@ -58,6 +59,8 @@ const ( RoleARNKey = "role_arn" SigV4Auth = "sigv4auth" MetricsCollectionIntervalKey = "metrics_collection_interval" + CollectionIntervalKey = "collection_interval" + ClusterNameKey = "cluster_name" AggregationDimensionsKey = "aggregation_dimensions" MeasurementKey = "measurement" DropOriginalMetricsKey = "drop_original_metrics" @@ -543,3 +546,27 @@ func GetClusterName(conf *confmap.Conf) string { return util.GetClusterNameFromEc2Tagger() } + +// DefaultCollectionInterval is the default collection interval for OpenTelemetry-based features. +const DefaultCollectionInterval = 30 * time.Second + +// ClusterNameRegex restricts cluster_name to safe characters, preventing +// OTTL injection and template metacharacter issues. +var ClusterNameRegex = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`) + +// GetOtelClusterName returns the cluster name for OpenTelemetry-based features. +// It reads exclusively from opentelemetry::cluster_name. +func GetOtelClusterName(conf *confmap.Conf) string { + val, _ := GetString(conf, ConfigKey(OpenTelemetryKey, ClusterNameKey)) + return val +} + +// GetCollectionInterval returns the collection interval for an OpenTelemetry feature. +// It checks the feature-specific collection_interval field. Default is 30s. +func GetCollectionInterval(conf *confmap.Conf, featureKey string) time.Duration { + key := ConfigKey(featureKey, CollectionIntervalKey) + if v, ok := GetNumber(conf, key); ok && v > 0 { + return time.Duration(v) * time.Second + } + return DefaultCollectionInterval +} diff --git a/translator/translate/otel/common/otel_config_test.go b/translator/translate/otel/common/otel_config_test.go new file mode 100644 index 0000000000..ad750ec55f --- /dev/null +++ b/translator/translate/otel/common/otel_config_test.go @@ -0,0 +1,114 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package common + +import ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/confmap" +) + +func TestGetOtelClusterName(t *testing.T) { + testCases := map[string]struct { + input map[string]any + envVar string + want string + }{ + "FromOtelRootLevel": { + input: map[string]any{ + "opentelemetry": map[string]any{ + "cluster_name": "my-otel-cluster", + }, + }, + want: "my-otel-cluster", + }, + "EnvVarIgnored": { + input: map[string]any{}, + envVar: "env-cluster", + want: "", + }, + "EmptyConfig": { + input: map[string]any{}, + want: "", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + if tc.envVar != "" { + os.Setenv("K8S_CLUSTER_NAME", tc.envVar) + defer os.Unsetenv("K8S_CLUSTER_NAME") + } else { + os.Unsetenv("K8S_CLUSTER_NAME") + } + + conf := confmap.NewFromStringMap(tc.input) + got := GetOtelClusterName(conf) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestGetCollectionInterval(t *testing.T) { + testCases := map[string]struct { + input map[string]any + featureKey string + want time.Duration + }{ + "FeatureSpecificCollectionInterval": { + input: map[string]any{ + "opentelemetry": map[string]any{ + "collect": map[string]any{ + "container_insights": map[string]any{ + "collection_interval": 45, + }, + }, + }, + }, + featureKey: "opentelemetry::collect::container_insights", + want: 45 * time.Second, + }, + "Default30sWhenNotSet": { + input: map[string]any{}, + featureKey: "opentelemetry::collect::container_insights", + want: 30 * time.Second, + }, + "ZeroValueIgnored": { + input: map[string]any{ + "opentelemetry": map[string]any{ + "collect": map[string]any{ + "container_insights": map[string]any{ + "collection_interval": 0, + }, + }, + }, + }, + featureKey: "opentelemetry::collect::container_insights", + want: 30 * time.Second, + }, + "NegativeValueIgnored": { + input: map[string]any{ + "opentelemetry": map[string]any{ + "collect": map[string]any{ + "container_insights": map[string]any{ + "collection_interval": -5, + }, + }, + }, + }, + featureKey: "opentelemetry::collect::container_insights", + want: 30 * time.Second, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + conf := confmap.NewFromStringMap(tc.input) + got := GetCollectionInterval(conf, tc.featureKey) + require.Equal(t, tc.want, got) + }) + } +} diff --git a/translator/translate/otel/pipeline/opentelemetry/containerinsights/common.go b/translator/translate/otel/pipeline/opentelemetry/containerinsights/common.go index 4aeb66d17a..f5b60b8e1a 100644 --- a/translator/translate/otel/pipeline/opentelemetry/containerinsights/common.go +++ b/translator/translate/otel/pipeline/opentelemetry/containerinsights/common.go @@ -6,7 +6,6 @@ package containerinsights import ( "fmt" "os" - "regexp" "strings" "time" @@ -18,9 +17,7 @@ import ( ) const ( - ciPrefix = "cw_k8s_ci_v0" - defaultCollectionInterval = 30 * time.Second - + ciPrefix = "cw_k8s_ci_v0" modeNode = "node" modeCluster = "cluster" ) @@ -90,28 +87,19 @@ func (t *yamlComponentTranslator) Translate(_ *confmap.Conf) (component.Config, return t.cfg, nil } -// clusterNameRegex restricts cluster_name to safe characters, preventing -// OTTL injection and template metacharacter issues in YAML templates. -var clusterNameRegex = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`) - func getClusterName(conf *confmap.Conf) (string, error) { - key := common.ConfigKey(ciConfigKey, "cluster_name") - name, ok := common.GetString(conf, key) - if !ok || name == "" { - return "", fmt.Errorf("cluster_name is required for container_insights") + name := common.GetOtelClusterName(conf) + if name == "" { + return "", fmt.Errorf("cluster_name is required for container_insights: set opentelemetry::cluster_name in config") } - if !clusterNameRegex.MatchString(name) { - return "", fmt.Errorf("cluster_name contains invalid characters: %q (must match %s)", name, clusterNameRegex.String()) + if !common.ClusterNameRegex.MatchString(name) { + return "", fmt.Errorf("cluster_name contains invalid characters: %q (must match %s)", name, common.ClusterNameRegex.String()) } return name, nil } func getCollectionInterval(conf *confmap.Conf) time.Duration { - key := common.ConfigKey(ciConfigKey, "collection_interval") - if v, ok := common.GetNumber(conf, key); ok && v > 0 { - return time.Duration(v) * time.Second - } - return defaultCollectionInterval + return common.GetCollectionInterval(conf, ciConfigKey) } // logsEnabled returns true if container_insights.logs.enabled is set to true. diff --git a/translator/translate/otel/pipeline/opentelemetry/hostmetrics/translator.go b/translator/translate/otel/pipeline/opentelemetry/hostmetrics/translator.go index 60ccf01e8d..eab915419e 100644 --- a/translator/translate/otel/pipeline/opentelemetry/hostmetrics/translator.go +++ b/translator/translate/otel/pipeline/opentelemetry/hostmetrics/translator.go @@ -4,6 +4,8 @@ package hostmetrics import ( + "fmt" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/pipeline" @@ -57,12 +59,26 @@ func (t *hostMetricsTranslator) Translate(conf *confmap.Conf) (*common.Component fwdConnector := forward.NewTranslator(common.OpenTelemetryKey) + processors := common.NewTranslatorMap[component.Config, component.ID]() + processors.Set(transformprocessor.NewTranslatorWithName("host_metrics_scope", + transformprocessor.WithErrorMode("ignore"), + transformprocessor.WithMetricScopeStatements(common.ScopeStatementsForSolution("otel-host-metrics")), + )) + // Apply root-level cluster name if set + if clusterName := common.GetOtelClusterName(conf); clusterName != "" { + if !common.ClusterNameRegex.MatchString(clusterName) { + return nil, fmt.Errorf("cluster_name contains invalid characters: %q", clusterName) + } + processors.Set(transformprocessor.NewTranslatorWithName("set_cluster_name", + transformprocessor.WithMetricResourceStatements([]string{ + fmt.Sprintf(`set(resource.attributes["k8s.cluster.name"], "%s")`, clusterName), + }), + )) + } + return &common.ComponentTranslators{ - Receivers: common.NewTranslatorMap[component.Config, component.ID](hostmetrics.NewTranslator(opts...)), - Processors: common.NewTranslatorMap[component.Config, component.ID](transformprocessor.NewTranslatorWithName("host_metrics_scope", - transformprocessor.WithErrorMode("ignore"), - transformprocessor.WithMetricScopeStatements(common.ScopeStatementsForSolution("otel-host-metrics")), - )), + Receivers: common.NewTranslatorMap[component.Config, component.ID](hostmetrics.NewTranslator(opts...)), + Processors: processors, Exporters: common.NewTranslatorMap[component.Config, component.ID](fwdConnector), Extensions: common.NewTranslatorMap[component.Config, component.ID](), Connectors: common.NewTranslatorMap[component.Config, component.ID](fwdConnector), diff --git a/translator/translate/otel/pipeline/opentelemetry/otlp/translator.go b/translator/translate/otel/pipeline/opentelemetry/otlp/translator.go index d439b1721c..e07535e37e 100644 --- a/translator/translate/otel/pipeline/opentelemetry/otlp/translator.go +++ b/translator/translate/otel/pipeline/opentelemetry/otlp/translator.go @@ -4,6 +4,8 @@ package otlp import ( + "fmt" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/pipeline" @@ -58,6 +60,25 @@ func (t *otlpPipelineTranslator) Translate(conf *confmap.Conf) (*common.Componen transformprocessor.WithErrorMode("ignore"), transformprocessor.WithScopeStatements(common.ScopeStatementsForSolution("otel-otlp")), )) + // Apply root-level cluster name if set + if clusterName := common.GetOtelClusterName(conf); clusterName != "" { + if !common.ClusterNameRegex.MatchString(clusterName) { + return nil, fmt.Errorf("cluster_name contains invalid characters: %q", clusterName) + } + stmt := fmt.Sprintf(`set(resource.attributes["k8s.cluster.name"], "%s")`, clusterName) + switch t.signal { + case pipeline.SignalMetrics: + processors.Set(transformprocessor.NewTranslatorWithName("set_cluster_name", + transformprocessor.WithMetricResourceStatements([]string{stmt}), + )) + case pipeline.SignalLogs: + processors.Set(transformprocessor.NewTranslatorWithName("set_cluster_name_logs", + transformprocessor.WithLogResourceStatements([]string{stmt}), + )) + // Traces are not handled: WithTraceResourceStatements is not yet supported. + // Traces sent via OTLP on EKS should include k8s.cluster.name from the SDK. + } + } if t.signal == pipeline.SignalLogs { processors.Set(transformprocessor.NewTranslatorWithName("otlp_log_source", transformprocessor.WithLogResourceStatements([]string{ diff --git a/translator/translate/otel/pipeline/opentelemetry/prometheus/translator.go b/translator/translate/otel/pipeline/opentelemetry/prometheus/translator.go index 920e17d421..88e6b02b70 100644 --- a/translator/translate/otel/pipeline/opentelemetry/prometheus/translator.go +++ b/translator/translate/otel/pipeline/opentelemetry/prometheus/translator.go @@ -7,7 +7,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver" "go.opentelemetry.io/collector/component" @@ -24,11 +23,8 @@ const ( pipelineName = "otel_prometheus" ) -var clusterNameRegex = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`) - var prometheusKey = common.ConfigKey(common.OpenTelemetryKey, common.CollectKey, common.PrometheusKey) var configPathKey = common.ConfigKey(prometheusKey, "config_path") -var clusterNameKey = common.ConfigKey(prometheusKey, "cluster_name") type translator struct{} @@ -55,8 +51,9 @@ func (t *translator) Translate(conf *confmap.Conf) (*common.ComponentTranslators transformprocessor.WithErrorMode("ignore"), transformprocessor.WithMetricScopeStatements(common.ScopeStatementsForSolution("otel-prometheus")), )) - if clusterName, ok := common.GetString(conf, clusterNameKey); ok && clusterName != "" { - if !clusterNameRegex.MatchString(clusterName) { + // Apply root-level cluster name if set (opentelemetry::cluster_name) + if clusterName := common.GetOtelClusterName(conf); clusterName != "" { + if !common.ClusterNameRegex.MatchString(clusterName) { return nil, fmt.Errorf("cluster_name contains invalid characters: %q", clusterName) } processors.Set(transformprocessor.NewTranslatorWithName("set_cluster_name", diff --git a/translator/translate/otel/pipeline/opentelemetry/prometheus/translator_test.go b/translator/translate/otel/pipeline/opentelemetry/prometheus/translator_test.go index 0fe6c31a75..7a0f04dd30 100644 --- a/translator/translate/otel/pipeline/opentelemetry/prometheus/translator_test.go +++ b/translator/translate/otel/pipeline/opentelemetry/prometheus/translator_test.go @@ -35,6 +35,7 @@ func TestPrometheusTranslator(t *testing.T) { "WithValidConfig": { input: map[string]interface{}{ "opentelemetry": map[string]interface{}{ + "cluster_name": "test-cluster", "collect": map[string]interface{}{ "prometheus": map[string]interface{}{ "config_path": createTempPromConfig(t), @@ -47,10 +48,10 @@ func TestPrometheusTranslator(t *testing.T) { "WithClusterName": { input: map[string]interface{}{ "opentelemetry": map[string]interface{}{ + "cluster_name": "my-cluster", "collect": map[string]interface{}{ "prometheus": map[string]interface{}{ - "config_path": createTempPromConfig(t), - "cluster_name": "my-cluster", + "config_path": createTempPromConfig(t), }, }, }, @@ -60,10 +61,10 @@ func TestPrometheusTranslator(t *testing.T) { "WithInvalidClusterName": { input: map[string]interface{}{ "opentelemetry": map[string]interface{}{ + "cluster_name": `bad"name`, "collect": map[string]interface{}{ "prometheus": map[string]interface{}{ - "config_path": createTempPromConfig(t), - "cluster_name": `bad"name`, + "config_path": createTempPromConfig(t), }, }, }, @@ -73,6 +74,7 @@ func TestPrometheusTranslator(t *testing.T) { "WithMissingConfigFile": { input: map[string]interface{}{ "opentelemetry": map[string]interface{}{ + "cluster_name": "test-cluster", "collect": map[string]interface{}{ "prometheus": map[string]interface{}{ "config_path": "/nonexistent/path.yml", @@ -109,10 +111,10 @@ func TestPrometheusTranslator(t *testing.T) { func TestPrometheusTranslatorClusterNameProcessor(t *testing.T) { conf := confmap.NewFromStringMap(map[string]interface{}{ "opentelemetry": map[string]interface{}{ + "cluster_name": "test-cluster", "collect": map[string]interface{}{ "prometheus": map[string]interface{}{ - "config_path": createTempPromConfig(t), - "cluster_name": "test-cluster", + "config_path": createTempPromConfig(t), }, }, }, @@ -231,10 +233,10 @@ func TestPrometheusTranslatorK8sMode(t *testing.T) { conf := confmap.NewFromStringMap(map[string]interface{}{ "opentelemetry": map[string]interface{}{ + "cluster_name": "test-cluster", "collect": map[string]interface{}{ "prometheus": map[string]interface{}{ - "config_path": createTempPromConfig(t), - "cluster_name": "test-cluster", + "config_path": createTempPromConfig(t), }, }, }, diff --git a/translator/translate/otel/receiver/hostmetrics/translator.go b/translator/translate/otel/receiver/hostmetrics/translator.go index 31a2a4cdf1..a24a8ffa3b 100644 --- a/translator/translate/otel/receiver/hostmetrics/translator.go +++ b/translator/translate/otel/receiver/hostmetrics/translator.go @@ -94,7 +94,7 @@ func (t *translator) Translate(conf *confmap.Conf) (component.Config, error) { } intervalKeyChain := []string{ - common.ConfigKey(common.OpenTelemetryKey, common.CollectKey, common.HostMetricsKey, common.MetricsCollectionIntervalKey), + common.ConfigKey(common.OpenTelemetryKey, common.CollectKey, common.HostMetricsKey, common.CollectionIntervalKey), common.ConfigKey(common.AgentKey, common.MetricsCollectionIntervalKey), } return &Config{ diff --git a/translator/translate/otel/receiver/hostmetrics/translator_test.go b/translator/translate/otel/receiver/hostmetrics/translator_test.go index 11eb24c322..232876437b 100644 --- a/translator/translate/otel/receiver/hostmetrics/translator_test.go +++ b/translator/translate/otel/receiver/hostmetrics/translator_test.go @@ -45,7 +45,7 @@ func TestTranslate(t *testing.T) { "opentelemetry": map[string]interface{}{ "collect": map[string]interface{}{ "host_metrics": map[string]interface{}{ - "metrics_collection_interval": 10, + "collection_interval": 10, }, }, }, @@ -60,7 +60,7 @@ func TestTranslate(t *testing.T) { "opentelemetry": map[string]interface{}{ "collect": map[string]interface{}{ "host_metrics": map[string]interface{}{ - "metrics_collection_interval": 10, + "collection_interval": 10, }, }, },