diff --git a/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent.go b/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent.go index 97c1f123cf7..0627d6b0c10 100644 --- a/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent.go +++ b/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent.go @@ -37,7 +37,6 @@ import ( "github.com/aws/amazon-cloudwatch-agent/cmd/amazon-cloudwatch-agent/internal" "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/useragent" "github.com/aws/amazon-cloudwatch-agent/internal/mapstructure" - "github.com/aws/amazon-cloudwatch-agent/internal/merge/confmap" "github.com/aws/amazon-cloudwatch-agent/internal/version" cwaLogger "github.com/aws/amazon-cloudwatch-agent/logger" "github.com/aws/amazon-cloudwatch-agent/logs" @@ -348,11 +347,16 @@ func runAgent(ctx context.Context, otelConfigs := fOtelConfigs // try merging configs together, will return nil if nothing to merge - merged, err := mergeConfigs(otelConfigs) + merged, err := mergeConfigs(otelConfigs, envconfig.IsUsageDataEnabled()) if err != nil { return err } if merged != nil { + if _, err = os.Stat(paths.YamlConfigPath); err == nil { + useragent.Get().AddFeatureFlags(featureFlagOtelMergeJSON) + } else { + useragent.Get().AddFeatureFlags(featureFlagOtelMergeYAML) + } _ = os.Setenv(envconfig.CWAgentMergedOtelConfig, toyamlconfig.ToYamlConfig(merged.ToStringMap())) otelConfigs = []string{"env:" + envconfig.CWAgentMergedOtelConfig} } else { @@ -418,37 +422,6 @@ func getCollectorParams(factories otelcol.Factories, providerSettings otelcol.Co } } -// mergeConfigs tries to merge configurations together. If nothing to merge, returns nil without an error. -func mergeConfigs(configPaths []string) (*confmap.Conf, error) { - var loaders []confmap.Loader - if envconfig.IsRunningInContainer() { - content, ok := os.LookupEnv(envconfig.CWOtelConfigContent) - if ok && len(content) > 0 { - log.Printf("D! Merging OTEL configuration from: %s", envconfig.CWOtelConfigContent) - loaders = append(loaders, confmap.NewByteLoader(envconfig.CWOtelConfigContent, []byte(content))) - } - } - // If using environment variable or passing in more than one config - if len(loaders) > 0 || len(configPaths) > 1 { - log.Printf("D! Merging OTEL configurations from: %s", configPaths) - for _, configPath := range configPaths { - loaders = append(loaders, confmap.NewFileLoader(configPath)) - } - result := confmap.New() - for _, loader := range loaders { - conf, err := loader.Load() - if err != nil { - return nil, fmt.Errorf("failed to load OTEL configs: %w", err) - } - if err = result.Merge(conf); err != nil { - return nil, fmt.Errorf("failed to merge OTEL configs: %w", err) - } - } - return result, nil - } - return nil, nil -} - func components(telegrafConfig *config.Config) (otelcol.Factories, error) { telegrafAdapter := adapter.NewAdapter(telegrafConfig) diff --git a/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent_test.go b/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent_test.go index 548ba239a64..aa45df23ec6 100644 --- a/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent_test.go +++ b/cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent_test.go @@ -17,8 +17,6 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" - "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" - "github.com/aws/amazon-cloudwatch-agent/internal/merge/confmap" "github.com/aws/amazon-cloudwatch-agent/logger" "github.com/aws/amazon-cloudwatch-agent/tool/paths" ) @@ -67,94 +65,6 @@ func Test_getCollectorParams(t *testing.T) { } } -func TestMergeConfigs(t *testing.T) { - testEnvValue := `receivers: - nop/1: -exporters: - nop: -extensions: - nop: -service: - extensions: [nop] - pipelines: - traces/1: - receivers: [nop/1] - exporters: [nop] -` - testCases := map[string]struct { - input []string - isContainer bool - envValue string - want *confmap.Conf - wantErr bool - }{ - "WithInvalidFile": { - input: []string{filepath.Join("not", "a", "file"), filepath.Join("testdata", "base.yaml")}, - wantErr: true, - }, - "WithNoMerge": { - input: []string{filepath.Join("testdata", "base.yaml")}, - wantErr: false, - }, - "WithoutEnv/Container": { - input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "merge.yaml")}, - isContainer: true, - want: mustLoadFromFile(t, filepath.Join("testdata", "base+merge.yaml")), - }, - "WithEnv/NonContainer": { - input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "merge.yaml")}, - isContainer: false, - envValue: testEnvValue, - want: mustLoadFromFile(t, filepath.Join("testdata", "base+merge.yaml")), - }, - "WithEnv/Container": { - input: []string{filepath.Join("testdata", "base.yaml")}, - isContainer: true, - envValue: testEnvValue, - want: mustLoadFromFile(t, filepath.Join("testdata", "base+env.yaml")), - }, - "WithEmptyEnv/Container": { - input: []string{filepath.Join("testdata", "base.yaml")}, - isContainer: true, - envValue: "", - want: nil, - wantErr: false, - }, - "WithInvalidEnv/Container": { - input: []string{filepath.Join("testdata", "base.yaml")}, - isContainer: true, - envValue: "test", - wantErr: true, - }, - "WithEnv/Container/MultipleFiles": { - input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "merge.yaml")}, - isContainer: true, - envValue: testEnvValue, - want: mustLoadFromFile(t, filepath.Join("testdata", "base+merge+env.yaml")), - }, - } - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - if testCase.isContainer { - t.Setenv(envconfig.RunInContainer, envconfig.TrueValue) - } - t.Setenv(envconfig.CWOtelConfigContent, testCase.envValue) - got, err := mergeConfigs(testCase.input) - if testCase.wantErr { - assert.Error(t, err) - assert.Nil(t, got) - } else if testCase.want == nil { - assert.NoError(t, err) - assert.Nil(t, got) - } else { - assert.NoError(t, err) - assert.NotNil(t, got) - assert.Equal(t, testCase.want.ToStringMap(), got.ToStringMap()) - } - }) - } -} - func TestFallbackOtelConfig(t *testing.T) { defaultYamlRelativePath := filepath.Join("default", paths.YAML) testCases := map[string]struct { @@ -200,9 +110,3 @@ func TestFallbackOtelConfig(t *testing.T) { }) } } - -func mustLoadFromFile(t *testing.T, path string) *confmap.Conf { - conf, err := confmap.NewFileLoader(path).Load() - require.NoError(t, err) - return conf -} diff --git a/cmd/amazon-cloudwatch-agent/merge.go b/cmd/amazon-cloudwatch-agent/merge.go new file mode 100644 index 00000000000..793d8b230a8 --- /dev/null +++ b/cmd/amazon-cloudwatch-agent/merge.go @@ -0,0 +1,215 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package main + +import ( + "errors" + "fmt" + "log" + "os" + "slices" + "strings" + + "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" + "github.com/aws/amazon-cloudwatch-agent/internal/merge/confmap" + agenthealthtranslator "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/agenthealth" +) + +const ( + featureFlagOtelMergeYAML = "otel_merge_yaml" + featureFlagOtelMergeJSON = "otel_merge_json" +) + +// mergeConfigs merges multiple OTEL configs together, including any config +// provided via the CW_CONFIG_CONTENT environment variable when running in a +// container. Returns nil without an error if there is nothing to merge (i.e. +// a single config path with no env override). In practice, a single config +// means no custom YAML was provided — the default agent YAML is always +// accompanied by at least one custom YAML when custom configs are in use. +func mergeConfigs(configPaths []string, isUsageDataEnabled bool) (*confmap.Conf, error) { + var loaders []confmap.Loader + if envconfig.IsRunningInContainer() { + content, ok := os.LookupEnv(envconfig.CWOtelConfigContent) + if ok && len(content) > 0 { + log.Printf("D! Merging OTEL configuration from: %s", envconfig.CWOtelConfigContent) + loaders = append(loaders, confmap.NewByteLoader(envconfig.CWOtelConfigContent, []byte(content))) + } + } + // If using environment variable or passing in more than one config + if len(loaders) > 0 || len(configPaths) > 1 { + log.Printf("D! Merging OTEL configurations from: %s", configPaths) + for _, configPath := range configPaths { + loaders = append(loaders, confmap.NewFileLoader(configPath)) + } + var result *confmap.Conf + for _, loader := range loaders { + conf, err := loader.Load() + if err != nil { + if errors.Is(err, os.ErrNotExist) { + log.Printf("D! Skipping non-existent OTEL config: %s", loader.ID()) + continue + } + return nil, fmt.Errorf("failed to load OTEL configs: %w", err) + } + if result == nil { + result = confmap.New() + } + if err = result.Merge(conf); err != nil { + return nil, fmt.Errorf("failed to merge OTEL configs: %w", err) + } + } + return mergeAgentHealth(result, isUsageDataEnabled), nil + } + return nil, nil +} + +type exporterInfo struct { + middlewareID string + operations []any +} + +var logsExporterInfo = exporterInfo{middlewareID: agenthealthtranslator.LogsID.String(), operations: []any{agenthealthtranslator.OperationPutLogEvents}} + +// supportedExporters maps exporter type names to their agenthealth middleware ID and operations. +var supportedExporters = map[string]exporterInfo{ + "awscloudwatch": {middlewareID: agenthealthtranslator.MetricsID.String(), operations: []any{agenthealthtranslator.OperationPutMetricData}}, + "awsemf": logsExporterInfo, + "awscloudwatchlogs": logsExporterInfo, + "awsxray": {middlewareID: agenthealthtranslator.TracesID.String(), operations: []any{agenthealthtranslator.OperationPutTraceSegments}}, +} + +// mergeAgentHealth scans the exporters in the config for supported AWS exporters +// and adds the appropriate agenthealth extension with a middleware reference to each. +// It also detects otlphttp exporters and sets their auth.authenticator to an +// agenthealth extension, chaining with any existing auth extension. +func mergeAgentHealth(conf *confmap.Conf, isUsageDataEnabled bool) *confmap.Conf { + if conf == nil || !isUsageDataEnabled { + return conf + } + + cfgMap := conf.ToStringMap() + + exporters, ok := cfgMap["exporters"].(map[string]any) + if !ok { + return conf + } + + // Track which agenthealth extensions are needed for AWS exporters + neededExtensions := make(map[string]exporterInfo) + for key := range exporters { + typeName, _, _ := strings.Cut(key, "/") + info, ok := supportedExporters[typeName] + if !ok { + continue + } + exporterCfg, ok := exporters[key].(map[string]any) + if !ok || exporterCfg == nil { + exporterCfg = make(map[string]any) + exporters[key] = exporterCfg + } + if _, alreadySet := exporterCfg["middleware"]; !alreadySet { + exporterCfg["middleware"] = info.middlewareID + neededExtensions[info.middlewareID] = info + } + } + + // Detect otlphttp exporters for auth-based agenthealth integration + type otlphttpAuthEntry struct { + exporterKey string + ahExtName string + additionalAuth string + } + var otlphttpEntries []otlphttpAuthEntry + for key := range exporters { + typeName, suffix, hasSuffix := strings.Cut(key, "/") + if typeName != "otlphttp" { + continue + } + ahName := "agenthealth/otlphttp" + if hasSuffix { + ahName = "agenthealth/otlphttp_" + suffix + } + exporterCfg, ok := exporters[key].(map[string]any) + if !ok || exporterCfg == nil { + exporterCfg = make(map[string]any) + exporters[key] = exporterCfg + } + // Skip if already using an agenthealth auth extension + var additionalAuth string + if authMap, ok := exporterCfg["auth"].(map[string]any); ok { + if authn, ok := authMap["authenticator"].(string); ok { + if strings.HasPrefix(authn, "agenthealth/") { + continue + } + additionalAuth = authn + } + } + otlphttpEntries = append(otlphttpEntries, otlphttpAuthEntry{ + exporterKey: key, + ahExtName: ahName, + additionalAuth: additionalAuth, + }) + } + + if len(neededExtensions) == 0 && len(otlphttpEntries) == 0 { + return conf + } + + // Ensure extensions section exists + extensions, _ := cfgMap["extensions"].(map[string]any) + if extensions == nil { + extensions = make(map[string]any) + cfgMap["extensions"] = extensions + } + + // Ensure service section exists + service, _ := cfgMap["service"].(map[string]any) + if service == nil { + service = make(map[string]any) + cfgMap["service"] = service + } + + var svcExtensions []any + if existing, ok := service["extensions"].([]any); ok { + svcExtensions = existing + } + + for middlewareID, info := range neededExtensions { + if _, exists := extensions[middlewareID]; !exists { + extensions[middlewareID] = map[string]any{ + "is_usage_data_enabled": true, + "stats": map[string]any{ + "operations": info.operations, + }, + } + } + if !slices.Contains(svcExtensions, any(middlewareID)) { + svcExtensions = append(svcExtensions, middlewareID) + } + } + + // Configure agenthealth auth for otlphttp exporters + for _, entry := range otlphttpEntries { + exporterCfg := exporters[entry.exporterKey].(map[string]any) + exporterCfg["auth"] = map[string]any{"authenticator": entry.ahExtName} + if _, exists := extensions[entry.ahExtName]; !exists { + extCfg := map[string]any{ + "is_usage_data_enabled": true, + "stats": map[string]any{ + "operations": []any{"*"}, + }, + } + if entry.additionalAuth != "" { + extCfg["additional_auth"] = entry.additionalAuth + } + extensions[entry.ahExtName] = extCfg + } + if !slices.Contains(svcExtensions, any(entry.ahExtName)) { + svcExtensions = append(svcExtensions, entry.ahExtName) + } + } + + service["extensions"] = svcExtensions + return confmap.NewFromStringMap(cfgMap) +} diff --git a/cmd/amazon-cloudwatch-agent/merge_test.go b/cmd/amazon-cloudwatch-agent/merge_test.go new file mode 100644 index 00000000000..872d95c3dce --- /dev/null +++ b/cmd/amazon-cloudwatch-agent/merge_test.go @@ -0,0 +1,468 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package main + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" + "github.com/aws/amazon-cloudwatch-agent/internal/merge/confmap" +) + +func TestMergeConfigs(t *testing.T) { + testEnvValue := `receivers: + nop/1: +exporters: + nop: +extensions: + nop: +service: + extensions: [nop] + pipelines: + traces/1: + receivers: [nop/1] + exporters: [nop] +` + testCases := map[string]struct { + input []string + isContainer bool + envValue string + want *confmap.Conf + wantErr bool + }{ + "WithInvalidFile": { + input: []string{filepath.Join("testdata", "invalid.yaml"), filepath.Join("testdata", "base.yaml")}, + wantErr: true, + }, + "WithAllMissingFiles": { + input: []string{filepath.Join("not", "a", "file"), filepath.Join("also", "not", "a", "file")}, + want: nil, + }, + "WithMissingFile": { + input: []string{filepath.Join("not", "a", "file"), filepath.Join("testdata", "base.yaml")}, + want: mustLoadFromFile(t, filepath.Join("testdata", "base.yaml")), + }, + "WithNoMerge": { + input: []string{filepath.Join("testdata", "base.yaml")}, + wantErr: false, + }, + "WithoutEnv/Container": { + input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "merge.yaml")}, + isContainer: true, + want: mustLoadFromFile(t, filepath.Join("testdata", "base+merge.yaml")), + }, + "WithEnv/NonContainer": { + input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "merge.yaml")}, + isContainer: false, + envValue: testEnvValue, + want: mustLoadFromFile(t, filepath.Join("testdata", "base+merge.yaml")), + }, + "WithEnv/Container": { + input: []string{filepath.Join("testdata", "base.yaml")}, + isContainer: true, + envValue: testEnvValue, + want: mustLoadFromFile(t, filepath.Join("testdata", "base+env.yaml")), + }, + "WithEmptyEnv/Container": { + input: []string{filepath.Join("testdata", "base.yaml")}, + isContainer: true, + envValue: "", + want: nil, + wantErr: false, + }, + "WithInvalidEnv/Container": { + input: []string{filepath.Join("testdata", "base.yaml")}, + isContainer: true, + envValue: "test", + wantErr: true, + }, + "WithEnv/Container/MultipleFiles": { + input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "merge.yaml")}, + isContainer: true, + envValue: testEnvValue, + want: mustLoadFromFile(t, filepath.Join("testdata", "base+merge+env.yaml")), + }, + "WithAgentHealth": { + input: []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "awsemf.yaml")}, + want: mustLoadFromFile(t, filepath.Join("testdata", "base+awsemf.yaml")), + }, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + if testCase.isContainer { + t.Setenv(envconfig.RunInContainer, envconfig.TrueValue) + } + t.Setenv(envconfig.CWOtelConfigContent, testCase.envValue) + got, err := mergeConfigs(testCase.input, true) + if testCase.wantErr { + assert.Error(t, err) + assert.Nil(t, got) + } else if testCase.want == nil { + assert.NoError(t, err) + assert.Nil(t, got) + } else { + assert.NoError(t, err) + assert.NotNil(t, got) + assert.Equal(t, testCase.want.ToStringMap(), got.ToStringMap()) + } + }) + } +} + +func TestMergeConfigs_UsageDataDisabled(t *testing.T) { + got, err := mergeConfigs( + []string{filepath.Join("testdata", "base.yaml"), filepath.Join("testdata", "awsemf.yaml")}, + false, + ) + require.NoError(t, err) + require.NotNil(t, got) + assertNoExtensions(t, got.ToStringMap()) +} + +func TestMergeAgentHealth_NilConf(t *testing.T) { + assert.Nil(t, mergeAgentHealth(nil, true)) +} + +func TestMergeAgentHealth_NoExporters(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "receivers": map[string]any{"otlp": map[string]any{}}, + }) + got := mergeAgentHealth(conf, true) + assertNoExtensions(t, got.ToStringMap()) +} + +func TestMergeAgentHealth_NoAWSExporters(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "debug": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true) + assertNoExtensions(t, got.ToStringMap()) +} + +func TestMergeAgentHealth_AWSSingleExporter(t *testing.T) { + testCases := map[string]struct { + exporterKey string + wantMiddleware string + wantOperations []any + }{ + "awsemf": {exporterKey: "awsemf", wantMiddleware: "agenthealth/logs", wantOperations: []any{"PutLogEvents"}}, + "awsemf_named": {exporterKey: "awsemf/custom", wantMiddleware: "agenthealth/logs", wantOperations: []any{"PutLogEvents"}}, + "awscloudwatchlogs": {exporterKey: "awscloudwatchlogs", wantMiddleware: "agenthealth/logs", wantOperations: []any{"PutLogEvents"}}, + "awsxray": {exporterKey: "awsxray", wantMiddleware: "agenthealth/traces", wantOperations: []any{"PutTraceSegments"}}, + "awsxray_named": {exporterKey: "awsxray/custom", wantMiddleware: "agenthealth/traces", wantOperations: []any{"PutTraceSegments"}}, + "awscloudwatch": {exporterKey: "awscloudwatch", wantMiddleware: "agenthealth/metrics", wantOperations: []any{"PutMetricData"}}, + "awscloudwatch_named": {exporterKey: "awscloudwatch/custom", wantMiddleware: "agenthealth/metrics", wantOperations: []any{"PutMetricData"}}, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + testCase.exporterKey: map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + // Check middleware set on exporter + exporters := got["exporters"].(map[string]any) + expCfg := exporters[testCase.exporterKey].(map[string]any) + assert.Equal(t, testCase.wantMiddleware, expCfg["middleware"]) + + // Check extension definition with stats + extensions := getExtensions(t, got) + extCfg := extensions[testCase.wantMiddleware].(map[string]any) + assert.Equal(t, true, extCfg["is_usage_data_enabled"]) + stats := extCfg["stats"].(map[string]any) + assert.Equal(t, testCase.wantOperations, stats["operations"]) + + // Check service extensions list + assert.Contains(t, getSvcExtensions(t, got), testCase.wantMiddleware) + }) + } +} + +func TestMergeAgentHealth_MultipleExporters(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{}, + "awsxray": map[string]any{}, + "awscloudwatch": map[string]any{}, + "debug": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + assert.Equal(t, "agenthealth/logs", exporters["awsemf"].(map[string]any)["middleware"]) + assert.Equal(t, "agenthealth/traces", exporters["awsxray"].(map[string]any)["middleware"]) + assert.Equal(t, "agenthealth/metrics", exporters["awscloudwatch"].(map[string]any)["middleware"]) + debugCfg := exporters["debug"].(map[string]any) + _, hasMiddleware := debugCfg["middleware"] + assert.False(t, hasMiddleware) + + svcExts := getSvcExtensions(t, got) + assert.Contains(t, svcExts, "agenthealth/logs") + assert.Contains(t, svcExts, "agenthealth/traces") + assert.Contains(t, svcExts, "agenthealth/metrics") +} + +func TestMergeAgentHealth_DoesNotOverwriteExistingMiddleware(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{ + "middleware": "custom/extension", + }, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + assert.Equal(t, "custom/extension", exporters["awsemf"].(map[string]any)["middleware"]) + assertNoExtensions(t, got) +} + +func TestMergeAgentHealth_PartialCustomMiddleware(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{"middleware": "custom/extension"}, + "awscloudwatchlogs": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + assert.Equal(t, "custom/extension", exporters["awsemf"].(map[string]any)["middleware"]) + assert.Equal(t, "agenthealth/logs", exporters["awscloudwatchlogs"].(map[string]any)["middleware"]) + + extensions := getExtensions(t, got) + _, hasAgentHealth := extensions["agenthealth/logs"] + assert.True(t, hasAgentHealth) + assert.Equal(t, 1, count(getSvcExtensions(t, got), "agenthealth/logs")) +} + +func TestMergeAgentHealth_PreservesExistingExtensions(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{}, + }, + "extensions": map[string]any{ + "health_check": map[string]any{}, + }, + "service": map[string]any{ + "extensions": []any{"health_check"}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + extensions := getExtensions(t, got) + _, hasHealthCheck := extensions["health_check"] + assert.True(t, hasHealthCheck) + _, hasAgentHealth := extensions["agenthealth/logs"] + assert.True(t, hasAgentHealth) + + svcExts := getSvcExtensions(t, got) + assert.Contains(t, svcExts, "health_check") + assert.Contains(t, svcExts, "agenthealth/logs") +} + +func TestMergeAgentHealth_DoesNotDuplicateExtension(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{}, + "awscloudwatchlogs": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + assert.Equal(t, 1, count(getSvcExtensions(t, got), "agenthealth/logs")) +} + +func TestMergeAgentHealth_DoesNotOverwriteExistingAgentHealth(t *testing.T) { + existingCfg := map[string]any{"is_usage_data_enabled": false} + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{}, + }, + "extensions": map[string]any{ + "agenthealth/logs": existingCfg, + }, + "service": map[string]any{ + "extensions": []any{"agenthealth/logs"}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + extensions := getExtensions(t, got) + assert.Equal(t, existingCfg, extensions["agenthealth/logs"]) + + assert.Equal(t, 1, count(getSvcExtensions(t, got), "agenthealth/logs")) +} + +func TestMergeAgentHealth_OtlpHTTP_NoAuth(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{"endpoint": "https://example.com"}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + expCfg := exporters["otlphttp"].(map[string]any) + authMap := expCfg["auth"].(map[string]any) + assert.Equal(t, "agenthealth/otlphttp", authMap["authenticator"]) + + extensions := getExtensions(t, got) + extCfg := extensions["agenthealth/otlphttp"].(map[string]any) + assert.Equal(t, true, extCfg["is_usage_data_enabled"]) + stats := extCfg["stats"].(map[string]any) + assert.Equal(t, []any{"*"}, stats["operations"]) + _, hasAdditionalAuth := extCfg["additional_auth"] + assert.False(t, hasAdditionalAuth) + + assert.Contains(t, getSvcExtensions(t, got), "agenthealth/otlphttp") +} + +func TestMergeAgentHealth_OtlpHTTP_WithExistingAuth(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "auth": map[string]any{"authenticator": "sigv4auth"}, + }, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + expCfg := exporters["otlphttp"].(map[string]any) + authMap := expCfg["auth"].(map[string]any) + assert.Equal(t, "agenthealth/otlphttp", authMap["authenticator"]) + + extensions := getExtensions(t, got) + extCfg := extensions["agenthealth/otlphttp"].(map[string]any) + assert.Equal(t, "sigv4auth", extCfg["additional_auth"]) + assert.Equal(t, true, extCfg["is_usage_data_enabled"]) + stats := extCfg["stats"].(map[string]any) + assert.Equal(t, []any{"*"}, stats["operations"]) +} + +func TestMergeAgentHealth_OtlpHTTP_Named(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "otlphttp/custom": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + expCfg := exporters["otlphttp/custom"].(map[string]any) + authMap := expCfg["auth"].(map[string]any) + assert.Equal(t, "agenthealth/otlphttp_custom", authMap["authenticator"]) + + extensions := getExtensions(t, got) + _, exists := extensions["agenthealth/otlphttp_custom"] + assert.True(t, exists) + + assert.Contains(t, getSvcExtensions(t, got), "agenthealth/otlphttp_custom") +} + +func TestMergeAgentHealth_OtlpHTTP_AlreadyHasAgentHealth(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "auth": map[string]any{"authenticator": "agenthealth/existing"}, + }, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + expCfg := exporters["otlphttp"].(map[string]any) + authMap := expCfg["auth"].(map[string]any) + assert.Equal(t, "agenthealth/existing", authMap["authenticator"]) + + assertNoExtensions(t, got) +} + +func TestMergeAgentHealth_OtlpHTTP_MixedWithAWSExporters(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "awsemf": map[string]any{}, + "otlphttp": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + exporters := got["exporters"].(map[string]any) + assert.Equal(t, "agenthealth/logs", exporters["awsemf"].(map[string]any)["middleware"]) + authMap := exporters["otlphttp"].(map[string]any)["auth"].(map[string]any) + assert.Equal(t, "agenthealth/otlphttp", authMap["authenticator"]) + + extensions := getExtensions(t, got) + _, hasLogs := extensions["agenthealth/logs"] + assert.True(t, hasLogs) + _, hasOtlp := extensions["agenthealth/otlphttp"] + assert.True(t, hasOtlp) + + svcExts := getSvcExtensions(t, got) + assert.Contains(t, svcExts, "agenthealth/logs") + assert.Contains(t, svcExts, "agenthealth/otlphttp") +} + +func TestMergeAgentHealth_OtlpHTTP_OnlyOtlpHTTP(t *testing.T) { + conf := confmap.NewFromStringMap(map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{}, + }, + }) + got := mergeAgentHealth(conf, true).ToStringMap() + + extensions := getExtensions(t, got) + _, exists := extensions["agenthealth/otlphttp"] + assert.True(t, exists) + + assert.Contains(t, getSvcExtensions(t, got), "agenthealth/otlphttp") +} + +func getSvcExtensions(t *testing.T, m map[string]any) []any { + t.Helper() + svc, ok := m["service"].(map[string]any) + require.True(t, ok, "expected service section in config map") + exts, ok := svc["extensions"].([]any) + require.True(t, ok, "expected extensions list in service section") + return exts +} + +func getExtensions(t *testing.T, m map[string]any) map[string]any { + t.Helper() + exts, ok := m["extensions"].(map[string]any) + require.True(t, ok, "expected extensions section in config map") + return exts +} + +func assertNoExtensions(t *testing.T, m map[string]any) { + t.Helper() + _, hasExtensions := m["extensions"] + assert.False(t, hasExtensions) +} + +func count(slice []any, value any) int { + n := 0 + for _, v := range slice { + if v == value { + n++ + } + } + return n +} + +func mustLoadFromFile(t *testing.T, path string) *confmap.Conf { + conf, err := confmap.NewFileLoader(path).Load() + require.NoError(t, err) + return conf +} diff --git a/cmd/amazon-cloudwatch-agent/testdata/awsemf.yaml b/cmd/amazon-cloudwatch-agent/testdata/awsemf.yaml new file mode 100644 index 00000000000..6891d56b0a2 --- /dev/null +++ b/cmd/amazon-cloudwatch-agent/testdata/awsemf.yaml @@ -0,0 +1,11 @@ +receivers: + nop: + +exporters: + awsemf: + +service: + pipelines: + logs: + receivers: [nop] + exporters: [awsemf] diff --git a/cmd/amazon-cloudwatch-agent/testdata/base+awsemf.yaml b/cmd/amazon-cloudwatch-agent/testdata/base+awsemf.yaml new file mode 100644 index 00000000000..31e2d5e88bc --- /dev/null +++ b/cmd/amazon-cloudwatch-agent/testdata/base+awsemf.yaml @@ -0,0 +1,26 @@ +exporters: + awsemf: + middleware: agenthealth/logs + nop: +extensions: + agenthealth/logs: + is_usage_data_enabled: true + stats: + operations: + - PutLogEvents +receivers: + nop: +service: + extensions: + - agenthealth/logs + pipelines: + logs: + exporters: + - awsemf + receivers: + - nop + metrics: + exporters: + - nop + receivers: + - nop diff --git a/cmd/amazon-cloudwatch-agent/testdata/invalid.yaml b/cmd/amazon-cloudwatch-agent/testdata/invalid.yaml new file mode 100644 index 00000000000..e1bf27f57ae --- /dev/null +++ b/cmd/amazon-cloudwatch-agent/testdata/invalid.yaml @@ -0,0 +1 @@ +invalid: yaml: content: [ diff --git a/extension/agenthealth/config.go b/extension/agenthealth/config.go index 76125f2451d..b410c2111b1 100644 --- a/extension/agenthealth/config.go +++ b/extension/agenthealth/config.go @@ -17,6 +17,7 @@ type Config struct { Stats *agent.StatsConfig `mapstructure:"stats,omitempty"` IsStatusCodeEnabled bool `mapstructure:"is_status_code_enabled,omitempty"` UsageMetadata []metadata.Metadata `mapstructure:"usage_metadata,omitempty"` + AdditionalAuth *component.ID `mapstructure:"additional_auth,omitempty"` } var _ component.Config = (*Config)(nil) diff --git a/extension/agenthealth/extension.go b/extension/agenthealth/extension.go index dd847f1110c..45c51a098c0 100644 --- a/extension/agenthealth/extension.go +++ b/extension/agenthealth/extension.go @@ -4,8 +4,14 @@ package agenthealth import ( + "context" + "fmt" + "net/http" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension/extensionauth" + "go.opentelemetry.io/collector/extension/extensioncapabilities" "go.uber.org/zap" "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/stats" @@ -16,11 +22,13 @@ import ( type agentHealth struct { logger *zap.Logger cfg *Config - component.StartFunc + host component.Host component.ShutdownFunc } var _ awsmiddleware.Extension = (*agentHealth)(nil) +var _ extensionauth.HTTPClient = (*agentHealth)(nil) +var _ extensioncapabilities.Dependent = (*agentHealth)(nil) func (ah *agentHealth) Handlers() ([]awsmiddleware.RequestHandler, []awsmiddleware.ResponseHandler) { var responseHandlers []awsmiddleware.ResponseHandler @@ -54,6 +62,52 @@ func (ah *agentHealth) Handlers() ([]awsmiddleware.RequestHandler, []awsmiddlewa return requestHandlers, responseHandlers } +func (ah *agentHealth) Start(_ context.Context, host component.Host) error { + ah.host = host + return nil +} + +func (ah *agentHealth) Dependencies() []component.ID { + if ah.cfg.AdditionalAuth == nil { + return nil + } + return []component.ID{*ah.cfg.AdditionalAuth} +} + +func (ah *agentHealth) getAdditionalAuthExtension() (component.Component, error) { + if ah.cfg.AdditionalAuth == nil || ah.host == nil { + return nil, nil + } + ext := ah.host.GetExtensions()[*ah.cfg.AdditionalAuth] + if ext == nil { + return nil, fmt.Errorf("auth extension %v not found", ah.cfg.AdditionalAuth) + } + return ext, nil +} + +func (ah *agentHealth) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { + ext, err := ah.getAdditionalAuthExtension() + if err != nil { + return nil, err + } + if ext != nil { + httpClient, ok := ext.(extensionauth.HTTPClient) + if !ok { + return nil, fmt.Errorf("auth extension %v does not implement extensionauth.HTTPClient", ah.cfg.AdditionalAuth) + } + base, err = httpClient.RoundTripper(base) + if err != nil { + return nil, fmt.Errorf("failed to get RoundTripper from %v: %w", ah.cfg.AdditionalAuth, err) + } + } + requestHandlers, responseHandlers := ah.Handlers() + return &roundTripper{ + base: base, + requestHandlers: requestHandlers, + responseHandlers: responseHandlers, + }, nil +} + func NewAgentHealth(logger *zap.Logger, cfg *Config) awsmiddleware.Extension { return &agentHealth{logger: logger, cfg: cfg} } diff --git a/extension/agenthealth/extension_test.go b/extension/agenthealth/extension_test.go index d29be58aace..a6a366595c2 100644 --- a/extension/agenthealth/extension_test.go +++ b/extension/agenthealth/extension_test.go @@ -4,16 +4,42 @@ package agenthealth import ( + "bytes" "context" + "errors" + "io" + "net/http" "testing" + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/extension/extensionauth" + "go.opentelemetry.io/collector/extension/extensioncapabilities" "go.uber.org/zap" "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/stats/agent" ) +type mockAuthExtension struct { + component.StartFunc + component.ShutdownFunc + called bool + err error +} + +func (m *mockAuthExtension) RoundTripper(base http.RoundTripper) (http.RoundTripper, error) { + m.called = true + if m.err != nil { + return nil, m.err + } + return base, nil +} + +var _ extensionauth.HTTPClient = (*mockAuthExtension)(nil) + func TestExtension(t *testing.T) { ctx := context.Background() cfg := &Config{IsUsageDataEnabled: true, IsStatusCodeEnabled: true, Stats: &agent.StatsConfig{Operations: []string{"ListBuckets"}}} @@ -51,3 +77,125 @@ func TestExtensionStatusCodeOnly(t *testing.T) { assert.Len(t, responseHandlers, 0) assert.NoError(t, extension.Shutdown(ctx)) } + +func TestDependencies_Nil(t *testing.T) { + cfg := &Config{IsUsageDataEnabled: true} + ext := NewAgentHealth(zap.NewNop(), cfg) + assert.Nil(t, ext.(extensioncapabilities.Dependent).Dependencies()) +} + +func TestDependencies_WithAdditionalAuth(t *testing.T) { + authID := component.NewID(component.MustNewType("sigv4auth")) + cfg := &Config{IsUsageDataEnabled: true, AdditionalAuth: &authID} + ext := NewAgentHealth(zap.NewNop(), cfg) + deps := ext.(extensioncapabilities.Dependent).Dependencies() + assert.Equal(t, []component.ID{authID}, deps) +} + +func TestRoundTripper_NoInnerAuth(t *testing.T) { + cfg := &Config{IsUsageDataEnabled: true} + ext := NewAgentHealth(zap.NewNop(), cfg) + require.NoError(t, ext.Start(context.Background(), componenttest.NewNopHost())) + + var baseCalled bool + base := RoundTripFunc(func(*http.Request) (*http.Response, error) { + baseCalled = true + return &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader(nil))}, nil + }) + + rt, err := ext.(extensionauth.HTTPClient).RoundTripper(base) + require.NoError(t, err) + assert.NotNil(t, rt) + + req, err := http.NewRequest(http.MethodPost, "http://localhost/v1/metrics", nil) + require.NoError(t, err) + resp, err := rt.RoundTrip(req) + require.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) + assert.True(t, baseCalled) +} + +func TestRoundTripper_WithInnerAuth(t *testing.T) { + mockAuth := &mockAuthExtension{} + authID := component.NewID(component.MustNewType("mockauth")) + + mockHost := &awsmiddleware.MockExtensionsHost{} + mockHost.On("GetExtensions").Return(map[component.ID]component.Component{ + authID: mockAuth, + }) + + cfg := &Config{ + IsUsageDataEnabled: true, + AdditionalAuth: &authID, + } + ext := NewAgentHealth(zap.NewNop(), cfg) + require.NoError(t, ext.Start(context.Background(), mockHost)) + + base := RoundTripFunc(func(*http.Request) (*http.Response, error) { + return &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader(nil))}, nil + }) + + rt, err := ext.(extensionauth.HTTPClient).RoundTripper(base) + require.NoError(t, err) + assert.NotNil(t, rt) + assert.True(t, mockAuth.called) + mockHost.AssertExpectations(t) +} + +func TestRoundTripper_InnerAuthError(t *testing.T) { + mockAuth := &mockAuthExtension{err: errors.New("auth failed")} + authID := component.NewID(component.MustNewType("mockauth")) + + mockHost := &awsmiddleware.MockExtensionsHost{} + mockHost.On("GetExtensions").Return(map[component.ID]component.Component{ + authID: mockAuth, + }) + + cfg := &Config{ + IsUsageDataEnabled: true, + AdditionalAuth: &authID, + } + ext := NewAgentHealth(zap.NewNop(), cfg) + require.NoError(t, ext.Start(context.Background(), mockHost)) + + rt, err := ext.(extensionauth.HTTPClient).RoundTripper(http.DefaultTransport) + assert.Error(t, err) + assert.Nil(t, rt) + assert.True(t, mockAuth.called) +} + +func TestRoundTripper_ExtensionNotFound(t *testing.T) { + authID := component.NewID(component.MustNewType("mockauth")) + + mockHost := &awsmiddleware.MockExtensionsHost{} + mockHost.On("GetExtensions").Return(map[component.ID]component.Component{}) + + cfg := &Config{IsUsageDataEnabled: true, AdditionalAuth: &authID} + ext := NewAgentHealth(zap.NewNop(), cfg) + require.NoError(t, ext.Start(context.Background(), mockHost)) + + rt, err := ext.(extensionauth.HTTPClient).RoundTripper(http.DefaultTransport) + assert.ErrorContains(t, err, "not found") + assert.Nil(t, rt) +} + +func TestRoundTripper_NotHTTPClient(t *testing.T) { + authID := component.NewID(component.MustNewType("mockauth")) + + type nonHTTPExtension struct { + component.StartFunc + component.ShutdownFunc + } + mockHost := &awsmiddleware.MockExtensionsHost{} + mockHost.On("GetExtensions").Return(map[component.ID]component.Component{ + authID: &nonHTTPExtension{}, + }) + + cfg := &Config{IsUsageDataEnabled: true, AdditionalAuth: &authID} + ext := NewAgentHealth(zap.NewNop(), cfg) + require.NoError(t, ext.Start(context.Background(), mockHost)) + + rt, err := ext.(extensionauth.HTTPClient).RoundTripper(http.DefaultTransport) + assert.ErrorContains(t, err, "does not implement extensionauth.HTTPClient") + assert.Nil(t, rt) +} diff --git a/extension/agenthealth/roundtripper.go b/extension/agenthealth/roundtripper.go new file mode 100644 index 00000000000..eb15fd19fa4 --- /dev/null +++ b/extension/agenthealth/roundtripper.go @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package agenthealth + +import ( + "net/http" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + "github.com/google/uuid" +) + +var _ http.RoundTripper = (*roundTripper)(nil) + +type roundTripper struct { + base http.RoundTripper + requestHandlers []awsmiddleware.RequestHandler + responseHandlers []awsmiddleware.ResponseHandler +} + +func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + ctx := awsmiddleware.SetRequestID(req.Context(), uuid.NewString()) + ctx = awsmiddleware.SetOperationName(ctx, req.URL.Path) + req = req.WithContext(ctx) + req.Header.Del("User-Agent") + for _, h := range rt.requestHandlers { + h.HandleRequest(ctx, req) + } + resp, err := rt.base.RoundTrip(req) + if err != nil { + return nil, err + } + for _, h := range rt.responseHandlers { + h.HandleResponse(ctx, resp) + } + return resp, nil +} diff --git a/extension/agenthealth/roundtripper_test.go b/extension/agenthealth/roundtripper_test.go new file mode 100644 index 00000000000..058893df58b --- /dev/null +++ b/extension/agenthealth/roundtripper_test.go @@ -0,0 +1,104 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package agenthealth + +import ( + "bytes" + "context" + "errors" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type RoundTripFunc func(*http.Request) (*http.Response, error) + +func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} + +func TestRoundTripper_RequestHandlersCalled(t *testing.T) { + mockHandler := &awsmiddleware.MockHandler{} + mockHandler.On("ID").Maybe().Return("test") + mockHandler.On("Position").Maybe().Return(awsmiddleware.After) + mockHandler.On("HandleRequest", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + ctx := args.Get(0).(context.Context) + assert.NotEmpty(t, awsmiddleware.GetRequestID(ctx)) + assert.Equal(t, "/v1/metrics", awsmiddleware.GetOperationName(ctx)) + }).Return() + + base := RoundTripFunc(func(*http.Request) (*http.Response, error) { + return &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader(nil))}, nil + }) + + rt := &roundTripper{ + base: base, + requestHandlers: []awsmiddleware.RequestHandler{mockHandler}, + } + + req := httptest.NewRequest(http.MethodPost, "http://localhost/v1/metrics", nil) + resp, err := rt.RoundTrip(req) + require.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) + mockHandler.AssertCalled(t, "HandleRequest", mock.Anything, mock.Anything) +} + +func TestRoundTripper_ResponseHandlersCalled(t *testing.T) { + mockHandler := &awsmiddleware.MockHandler{} + mockHandler.On("ID").Maybe().Return("test") + mockHandler.On("Position").Maybe().Return(awsmiddleware.After) + mockHandler.On("HandleRequest", mock.Anything, mock.Anything).Return() + mockHandler.On("HandleResponse", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + ctx := args.Get(0).(context.Context) + assert.NotEmpty(t, awsmiddleware.GetRequestID(ctx)) + assert.Equal(t, "/v1/metrics", awsmiddleware.GetOperationName(ctx)) + }).Return() + + base := RoundTripFunc(func(*http.Request) (*http.Response, error) { + return &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader(nil))}, nil + }) + + rt := &roundTripper{ + base: base, + requestHandlers: []awsmiddleware.RequestHandler{mockHandler}, + responseHandlers: []awsmiddleware.ResponseHandler{mockHandler}, + } + + req := httptest.NewRequest(http.MethodPost, "http://localhost/v1/metrics", nil) + resp, err := rt.RoundTrip(req) + require.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) + mockHandler.AssertCalled(t, "HandleRequest", mock.Anything, mock.Anything) + mockHandler.AssertCalled(t, "HandleResponse", mock.Anything, mock.Anything) +} + +func TestRoundTripper_ErrorSkipsResponseHandlers(t *testing.T) { + mockHandler := &awsmiddleware.MockHandler{} + mockHandler.On("ID").Maybe().Return("test") + mockHandler.On("Position").Maybe().Return(awsmiddleware.After) + mockHandler.On("HandleRequest", mock.Anything, mock.Anything).Return() + + base := RoundTripFunc(func(*http.Request) (*http.Response, error) { + return nil, errors.New("connection refused") + }) + + rt := &roundTripper{ + base: base, + requestHandlers: []awsmiddleware.RequestHandler{mockHandler}, + responseHandlers: []awsmiddleware.ResponseHandler{mockHandler}, + } + + req := httptest.NewRequest(http.MethodPost, "http://localhost/v1/metrics", nil) + resp, err := rt.RoundTrip(req) + assert.Error(t, err) + assert.Nil(t, resp) + mockHandler.AssertCalled(t, "HandleRequest", mock.Anything, mock.Anything) + mockHandler.AssertNotCalled(t, "HandleResponse", mock.Anything, mock.Anything) +} diff --git a/go.mod b/go.mod index 772658bd308..0a1eb3640a5 100644 --- a/go.mod +++ b/go.mod @@ -9,49 +9,51 @@ 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/exporter/awscloudwatchlogsexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsemfexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20251211175346-5faee86d9fde + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsemfexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter => github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260320224757-7533ded50197 ) replace ( - github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/extension/awsproxy => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20251211175346-5faee86d9fde + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/awsproxy => github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260320224757-7533ded50197 ) replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/containerinsight => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/k8s => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/metrics => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/proxy => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/kubelet => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20251211175346-5faee86d9fde + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/awsutil => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/containerinsight => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/cwlogs => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/k8s => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/metrics => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/proxy => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/kubelet => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders => github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260320224757-7533ded50197 ) 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-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20251211175346-5faee86d9fde + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260320224757-7533ded50197 // 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-20251211175346-5faee86d9fde + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260320224757-7533ded50197 ) +replace github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws => github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260320224757-7533ded50197 + replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20251211175346-5faee86d9fde + github.com/open-telemetry/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor => github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260320224757-7533ded50197 ) replace ( - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20251211175346-5faee86d9fde - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20251211175346-5faee86d9fde + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260320224757-7533ded50197 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260320224757-7533ded50197 ) // Temporary fix, pending PR https://github.com/shirou/gopsutil/pull/957 @@ -102,15 +104,13 @@ replace github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.48.6 require ( github.com/BurntSushi/toml v1.3.2 github.com/Jeffail/gabs v1.4.0 - github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.124.0 + github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.124.1 github.com/aws/aws-sdk-go v1.55.7 - github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.30.2 github.com/bigkevmcd/go-configparser v0.0.0-20200217161103-d137835d2579 github.com/deckarep/golang-set/v2 v2.3.1 github.com/fsnotify/fsnotify v1.9.0 github.com/gin-gonic/gin v1.10.0 - github.com/go-kit/log v0.2.1 // indirect github.com/go-playground/validator/v10 v10.20.0 github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31 github.com/gobwas/glob v0.2.3 @@ -149,6 +149,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/processor/groupbytraceprocessor v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricsgenerationprocessor v0.124.1 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstarttimeprocessor v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstransformprocessor v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.124.1 @@ -160,10 +161,13 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsecscontainermetricsreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.124.1 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.124.1 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.124.1 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver v0.124.1 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tcplogreceiver v0.124.1 @@ -197,7 +201,10 @@ require ( go.opentelemetry.io/collector/exporter/debugexporter v0.124.0 go.opentelemetry.io/collector/exporter/exportertest v0.124.0 go.opentelemetry.io/collector/exporter/nopexporter v0.124.0 + go.opentelemetry.io/collector/exporter/otlphttpexporter v0.124.0 go.opentelemetry.io/collector/extension v1.30.0 + go.opentelemetry.io/collector/extension/extensionauth v1.30.0 + go.opentelemetry.io/collector/extension/extensioncapabilities v0.124.0 go.opentelemetry.io/collector/extension/extensiontest v0.124.0 go.opentelemetry.io/collector/extension/zpagesextension v0.124.0 go.opentelemetry.io/collector/filter v0.124.0 @@ -264,7 +271,7 @@ require ( github.com/alecthomas/participle v0.4.1 // indirect github.com/alecthomas/participle/v2 v2.1.4 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect - github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20251120035228-2492d12f5d81 // indirect + github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.124.1 // indirect github.com/antchfx/jsonquery v1.1.5 // indirect github.com/antchfx/xmlquery v1.4.4 // indirect github.com/antchfx/xpath v1.3.4 // indirect @@ -274,6 +281,7 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-msk-iam-sasl-signer-go v1.0.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect @@ -337,10 +345,11 @@ require ( github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -454,6 +463,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/metrics v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/proxy v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray v0.124.1 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/collectd v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.124.0 // indirect @@ -465,6 +475,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils v0.124.1 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.124.1 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.124.1 // indirect @@ -511,6 +522,7 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/tinylru v1.1.0 // indirect github.com/tidwall/wal v1.1.8 // indirect + github.com/tilinna/clock v1.1.0 // indirect github.com/tinylib/msgp v1.1.6 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect @@ -549,8 +561,6 @@ require ( go.opentelemetry.io/collector/consumer/xconsumer v0.124.0 // indirect go.opentelemetry.io/collector/exporter/exporterhelper/xexporterhelper v0.124.0 // indirect go.opentelemetry.io/collector/exporter/xexporter v0.124.0 // indirect - go.opentelemetry.io/collector/extension/extensionauth v1.30.0 // indirect - go.opentelemetry.io/collector/extension/extensioncapabilities v0.124.0 // indirect go.opentelemetry.io/collector/extension/xextension v0.124.0 // indirect go.opentelemetry.io/collector/featuregate v1.30.0 // indirect go.opentelemetry.io/collector/internal/fanoutconsumer v0.124.0 // indirect @@ -612,7 +622,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect - k8s.io/kubelet v0.30.0 // indirect + k8s.io/kubelet v0.32.3 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect modernc.org/sqlite v1.21.2 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/go.sum b/go.sum index ebe212c7e75..25ad6716804 100644 --- a/go.sum +++ b/go.sum @@ -185,60 +185,60 @@ 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/exporter/awscloudwatchlogsexporter v0.0.0-20251211175346-5faee86d9fde h1:cAugYFiLHu2qB4fgJpNHA6xSIpHuJ1H3Z2K7maOHF7A= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20251211175346-5faee86d9fde/go.mod h1:h/y8k16N0t89KjloCpZS8ow/H3sd4iU4w7U7anygxcc= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20251211175346-5faee86d9fde h1:kxyYQtlLB6HHfTSKT5belwUAGdng8swPpJSQIdxdYxw= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20251211175346-5faee86d9fde/go.mod h1:7ZWp3GCphI1A5TKBEzzl7kg6QukzNd9SQV4nQ2C6Teg= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20251211175346-5faee86d9fde h1:kOnIXGw2HmwMvdRx3/vvKIN8P9wpz4sdboFpFs/uS/c= -github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20251211175346-5faee86d9fde/go.mod h1:BAShbIsZmq4pndaVp6XfpS3ijVYHXkQhr4I89kQonTQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20251211175346-5faee86d9fde h1:fsUkuP9qi6nzsGhq61QJV75CgV7+OnH5DaHK7+ZplUw= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20251211175346-5faee86d9fde/go.mod h1:SpBtq2f0PclYNg6612tjc5MIZFUykEYyND0Tq7l0mzM= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20251211175346-5faee86d9fde h1:oySvdaLIYYpN/aJ5vXHetRaqggZndLgD2gUrQ9P9LG0= -github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20251211175346-5faee86d9fde/go.mod h1:orUknJg46Fszwz8LI733CI3uyku80NBdWqzzSgyzWKc= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20251211175346-5faee86d9fde h1:iV8PHkEDby6DJkxMvbomJvuJeeq83wE/2/9O8C2iSm4= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20251211175346-5faee86d9fde/go.mod h1:uQv03B/EZ5CVsYWrKqaJbiIMOY2DK6Ox2xjCved+YSM= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20251211175346-5faee86d9fde h1:KmaNFqKwC0Ssg9PZI7kCK1wFzSWYFo3F28/1FvQf/Pc= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20251211175346-5faee86d9fde/go.mod h1:UhjJJwNhJ2cQ1tQbFXgw3KVE4FsSp/C7KgJMr5c7Nvo= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20251211175346-5faee86d9fde h1:3h3/w458M9galLVsAK1qWzCXIGz+Ep4PlnKZ67GRG2M= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20251211175346-5faee86d9fde/go.mod h1:CdK/SDdJRDrk4G3YvlOWjbMR7BGAUUWEquHqVDLTzA4= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20251211175346-5faee86d9fde h1:AAzx4RHgZFDG5cvkjsMpuXlmKNunPDKeB4Ui2ag78iw= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20251211175346-5faee86d9fde/go.mod h1:Naqv+7HWtDgPbeT2CzUHStDFy3nQ55Y0WrtBPOvKp8Q= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20251211175346-5faee86d9fde h1:ar0RpPhTG9qQNTZZwSKCeYXxjWet+slVYC7ACy9Kl+g= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20251211175346-5faee86d9fde/go.mod h1:+HNk4dUzu9Fy7FqxdfvxNOoMzvYgVTzUfzVh1ApTKmI= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20251211175346-5faee86d9fde h1:LwVQhG+c+PxZSTl+CPwqrnMuEFbftaM1oIsL0V8JzDg= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20251211175346-5faee86d9fde/go.mod h1:cU6+bc9gLvH3AATXFf6dE5nEQZKeHXIlLiwTxDq3g7A= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20251211175346-5faee86d9fde h1:exdzXTEAbek2pLIms/JJgSVqfn+U53lojDs2w2gRSpU= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20251211175346-5faee86d9fde/go.mod h1:9VGjgdnP8thnbUWzTzOKwv0Ae8Rv6X5rzFY5LqRqNQQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20251211175346-5faee86d9fde h1:1887YwmM/JqUQrHjD3xuE/KTQtTLL8rPApo4eYazuCQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20251211175346-5faee86d9fde/go.mod h1:d9+1Jn/duaOcDCGl1fHnAgiMuxpesVyD2LanEIswoIY= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20251211175346-5faee86d9fde h1:iCBArvNHlGW6Lj6rwKb6WTSUZWKnJrbDK4VVtecAb+c= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20251211175346-5faee86d9fde/go.mod h1:DU5DozRx1wR9D4Vxwe/CRSNMwz15rub6r6TEo7rA2Fo= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20251211175346-5faee86d9fde h1:LFlw3yiJYZ9VXKjmw3OBzpry3OXwX0w6GQ4mN58wgZ8= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20251211175346-5faee86d9fde/go.mod h1:VAIY5q8ntx1MZREDXmVsQp/qIyqx3vq6UaPeWldNuRk= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20251211175346-5faee86d9fde h1:J+PntymXeKh7rCUeHShyrMI+Ee7r+meEQQIcQWSipSM= -github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20251211175346-5faee86d9fde/go.mod h1:aYprF4me3eU4vuCY4RcMF2+9JIbHX6+qpCs2NSKKke8= -github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20251120035228-2492d12f5d81 h1:e4N1Akig4dxFupuYCSrz03HUSA9YLYuX4ZrnA3HJEQQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20251120035228-2492d12f5d81/go.mod h1:TuE5ogQgw7qLh+Y+eKj3utx98fjnVZBNFb5P2C9pc+M= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20251211175346-5faee86d9fde h1:CgxJU8pD5En7ziYW6BJwbmDqUk5nknMiFykVr0eEGbY= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20251211175346-5faee86d9fde/go.mod h1:5F3wAhvG/R0QuE2G8523cXJ5d8s2Fs15dqcoL+jr8q8= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20251211175346-5faee86d9fde h1:e2YUU2vIlenOrkDGoIwLirbWWLkMbBawZxe4u2cAvss= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20251211175346-5faee86d9fde/go.mod h1:Gb2Ke8v8NMAJy/yz5QM+jb1XLBZQK4sHnIAQ6t/RodI= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20251211175346-5faee86d9fde h1:C6qi1qTlpL2sA3OpxUagwkal26/6Yg6xJ1n60Fl/ElU= -github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20251211175346-5faee86d9fde/go.mod h1:41eDZVHh5A1BLJ0Dy3oQIrS+du1Zu+rpKEijUazlnCI= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20251211175346-5faee86d9fde h1:zdRTe+hAu6vqcslCXIPw3Hhm/1FZu/szBZK9OX6u3Gc= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20251211175346-5faee86d9fde/go.mod h1:BlDVraekGuczBCDKtS2oFh9kKYcz2LOSNS8Z4vaPCgM= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20251211175346-5faee86d9fde h1:6uI0KNbl7eBQDNwISWpiMFzDmTtvjZqxnx9/mJD0IfI= -github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20251211175346-5faee86d9fde/go.mod h1:I41hi6XJwoAAauMtkaCQBtn0Fom+HE2KbIZjxauVavU= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20251211175346-5faee86d9fde h1:CkH9LExGkxV4NuEstRq3C7WTEfhMUWU2G5wikXLc0js= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20251211175346-5faee86d9fde/go.mod h1:D6z1zvQUW1wqb8gLcMy5Ud2oKhTS7Thq7/yXERXWXyA= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20251211175346-5faee86d9fde h1:8QnwbDUGO94UUn67Z95Hs8ElzC9VW2PguzFOoJvy1iM= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20251211175346-5faee86d9fde/go.mod h1:azh6UrFLEbpbmP3LJfaZW+hyiDykBg79KZAU4G6ICTE= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20251211175346-5faee86d9fde h1:vIC9dkU4l7AsgvXDYawPZAs9W8bEOCU42MA8rpjmdUc= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20251211175346-5faee86d9fde/go.mod h1:QogqM0I2YS5dR4AmjIquZMA+5RwoORbjV15uGRELuhQ= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20251211175346-5faee86d9fde h1:zQbOQpuSA4zMeNUztXpqatP/1HMTMemHhn3hBYlb0NI= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20251211175346-5faee86d9fde/go.mod h1:TYYNI6gpWY3x3Cjx1oE3d5Zazd81dvQXe7ENjCADIV4= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20251211175346-5faee86d9fde h1:N4rysf8R4P9zF7iAg3b/LNby8kwX0XYciOShLxX+GC0= -github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20251211175346-5faee86d9fde/go.mod h1:OYE9gY1A7SqPDdoLlAg72TWqi5yWSX2/czosSU2zypg= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260320224757-7533ded50197 h1:NXQ93zPOarJZIIY3u7vlHfc88S41q2pSpWNf2DI9a3w= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awscloudwatchlogsexporter v0.0.0-20260320224757-7533ded50197/go.mod h1:7yTvkr0wlHtp7C3LSZDYAlzh94MI4H5rjZKGrtfJfHI= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260320224757-7533ded50197 h1:ZsmA7dINnP6D9/qF+vx60Zl/rinn/2IN6BnqqUrWM9s= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsemfexporter v0.0.0-20260320224757-7533ded50197/go.mod h1:HMAQWk2DmjQKQ9O4ioTvJBAZZ4AOr7TwzwLR5Nciq2Y= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260320224757-7533ded50197 h1:QadR+GZ2jfTaK7y5wIrhFTpct0PcJ9C/F5RdNea0FZU= +github.com/amazon-contributing/opentelemetry-collector-contrib/exporter/awsxrayexporter v0.0.0-20260320224757-7533ded50197/go.mod h1:6hCU0l8HnBQHgy9lKssVuRcQCtF+0KkSuMOIgAk/LmE= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260320224757-7533ded50197 h1:hYPf8pY8KEzdI74qcB21rtRBeKhymld/0Sy6b9ZPLkU= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsmiddleware v0.0.0-20260320224757-7533ded50197/go.mod h1:kK2aqVUa/7Q35evNwjvSi4DvU5WNIRAWv5CgeNDI0sc= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260320224757-7533ded50197 h1:rpbD7eZsL2m7cSfQL4sDNQEUfIrc2WwUtHZhltHR7iY= +github.com/amazon-contributing/opentelemetry-collector-contrib/extension/awsproxy v0.0.0-20260320224757-7533ded50197/go.mod h1:SxvMY4MZLF8l+Bp64W0QhnLq6SYB8HeD/FdkhA6ohMg= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260320224757-7533ded50197 h1:D5GCicWTI3D30CqsB7pt5DLaHctHfMUX8BdYzfLaedI= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/awsutil v0.0.0-20260320224757-7533ded50197/go.mod h1:pYyQRPeq+yYt6noErZM6tHEYyDwNoQbOg/lYk17TTus= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260320224757-7533ded50197 h1:JSbthzp04r6i+7ge7s+USXsDvKuoHShCeDT4/ezhV/8= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/containerinsight v0.0.0-20260320224757-7533ded50197/go.mod h1:m0EeFfNFTrDiiSqsNeWyK0J/6UTZWxj38EB1+fxCEhM= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260320224757-7533ded50197 h1:uEqChepYL3FKjWMa7NeCBTPD18fLuPzbH//RTFZ/7R0= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/cwlogs v0.0.0-20260320224757-7533ded50197/go.mod h1:Kp9atsyCL2PCA2BsEre5oKt//l7sDn3GUCagq14p9g8= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260320224757-7533ded50197 h1:Y6fNruzMHyFq78Jz/eCMHMZny/nhxNR/6rtjee/bx1I= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/k8s v0.0.0-20260320224757-7533ded50197/go.mod h1:k0Ae8wOq18kgqZYGEkWLAWVEnsiCsZN4EnsHK6aW0l8= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260320224757-7533ded50197 h1:stRK5cOfMyZNePtGNOBw/b0jdU1tIbXOoG8YwFLxKiE= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/metrics v0.0.0-20260320224757-7533ded50197/go.mod h1:SDwXpZaJchM+ZM4jDQlxw/N8bfeT2whe2arEVH8jLDA= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260320224757-7533ded50197 h1:OKZSUWxQ6SSjesui4Dm/zJ0zkwmc9qBCeV2Mi4gnopQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/proxy v0.0.0-20260320224757-7533ded50197/go.mod h1:wVBEZmjivEYZ+ZM4Y0gk5IHekN4UwzpaDS+Yd6Rc5bk= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260320224757-7533ded50197 h1:UdRGJ2pFeCzlGlxnMP7PuFCgpuO4jsH2AamLjEhCqTQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/aws/xray v0.0.0-20260320224757-7533ded50197/go.mod h1:rmBHDERKTAjJIUaWuflVgpKxbt20t9lc4y043Wy/U9k= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260320224757-7533ded50197 h1:QFHfHBfVMBUAMsgaumoCqtsHVRwhxNaiHT0gjD19jx8= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/coreinternal v0.0.0-20260320224757-7533ded50197/go.mod h1:6jrYek7nhwm74CGimbM3A7N0HHb7XBvkIl8CAXcpHUo= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260320224757-7533ded50197 h1:n99fVKOVhDf2oedZfTFX2woIgI8giFPzr5EptdeyAkQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/k8sconfig v0.0.0-20260320224757-7533ded50197/go.mod h1:Tb0DzZot2gVdaGtjxUKZzvPpT7BWG0+f8o5oKA4QJck= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260320224757-7533ded50197 h1:hh72XSeaavxJHQpjaDj+X19JydHY3FrfDy3jfqVkUvE= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/kubelet v0.0.0-20260320224757-7533ded50197/go.mod h1:LOLm/7xatIi/S6R5112dzvGFPmvqTFfhCMYlomhyb5w= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260320224757-7533ded50197 h1:rtFh+RZIihjl97551HCl07lPGUdPRoI9TT2RL4x/iF0= +github.com/amazon-contributing/opentelemetry-collector-contrib/internal/metadataproviders v0.0.0-20260320224757-7533ded50197/go.mod h1:1hfg5X/6bS9vjhKi4P6d8OgL34SNMwXB0LdXGctO0wU= +github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260320224757-7533ded50197 h1:d9hPW7FdVNl19dXt2cTWVlgcB1wHNM1kZfcfwCvKX9k= +github.com/amazon-contributing/opentelemetry-collector-contrib/override/aws v0.0.0-20260320224757-7533ded50197/go.mod h1:045z/hu1f07Kcs+nkOrNi1tPYfdSrBVcs9xjdsSm1qo= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260320224757-7533ded50197 h1:K9Z5HG1cKDBYDrxHB2NLlnvU3/1mwl8EIFEl59FHjCM= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.0.0-20260320224757-7533ded50197/go.mod h1:M9oIU4qLCnliKaZBX7BH1CuanxxRh1KwKJk08snVk8U= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260320224757-7533ded50197 h1:yEwdltnxIj9lnKKsR9B5ARDOIduSVIyreZZsWWFD8ig= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/stanza v0.0.0-20260320224757-7533ded50197/go.mod h1:9nc546lH9vSNXIZ8ZxV4jeHFezk2pjmXOsyeEgprcKc= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260320224757-7533ded50197 h1:ttrQ58Z59lLFyxfEHnGAE9vMyT0ZGFSoVLr/NHtVzjM= +github.com/amazon-contributing/opentelemetry-collector-contrib/pkg/translator/prometheus v0.0.0-20260320224757-7533ded50197/go.mod h1:1vRBbzm/N04ZF/Xdvr+vR+lZw1ChXVL/HYtX+QgSuC8= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260320224757-7533ded50197 h1:lIoDO8Xlfznb7adfSoI8rQIw2lOw/WdRWSlNy+j0nQo= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.0.0-20260320224757-7533ded50197/go.mod h1:ZLmYbz7rTTxTd4AC40k4kiz7uOXA3oaU3ltvQqD2L6Q= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260320224757-7533ded50197 h1:qOoK5MusFKpVymWz5dz4Q+lrpnSYFMstmeNWnBwPbtg= +github.com/amazon-contributing/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.0.0-20260320224757-7533ded50197/go.mod h1:DB+M8FKzylfTJtLumS0+WkODeBbyA4H4Swn8qS7YUtE= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260320224757-7533ded50197 h1:4UvnlqUjOqeYA6gYseZOi70p3wwkPmnDCyhFcOqDyW8= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver v0.0.0-20260320224757-7533ded50197/go.mod h1:DUBtynlqXYA6tPGFlbqF57OGZ8+VWDpRu3GgRYk3Ua8= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260320224757-7533ded50197 h1:Q+e4Y3wbED9iprnhSjC1E4g8MOYkgW9H+yJ+tS07Dzc= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver v0.0.0-20260320224757-7533ded50197/go.mod h1:Xq0Nf1u9TpLTEG3QOw7LuYszU348Ou9DBBTO0wpHCSo= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260320224757-7533ded50197 h1:xCtBspw8HBoSRhlFU2Wm1KMj/YoVJF6ETEyo7WZRLZQ= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/awsxrayreceiver v0.0.0-20260320224757-7533ded50197/go.mod h1:VFIMswUWhjMQKQ2bYLJuYVxXiPyQWA22BQj0h9kmBMo= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260320224757-7533ded50197 h1:uBjA/F/T9KCfG2rhjgPodDvqXVM98l767qTZbsDIpSs= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/jmxreceiver v0.0.0-20260320224757-7533ded50197/go.mod h1:4YkOEJHjqTHWbbbH4Ert2MKSIRx6eFAhr/qoPpCWSx0= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260320224757-7533ded50197 h1:pACczdr3NutIKTAQe9NrNoyZOG+IyNCoSPUAbpCdilw= +github.com/amazon-contributing/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.0.0-20260320224757-7533ded50197/go.mod h1:ex2GsSuqQl8ZNFPMnVU2oFeJSai8cZmgd6F4/k2zOQA= 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.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= @@ -592,8 +592,9 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= @@ -1236,6 +1237,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/file github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.124.1/go.mod h1:BTHYA2guZgstbEQiIjx72OCVWzzI5WBFzuakZxcwjeY= github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil v0.124.1 h1:IXiiog4CkQWu/wSfiyYHpKGVgOa9BPlt9lD5YeYJTJg= github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil v0.124.1/go.mod h1:Hj+DbabX9lrGYwcdp6eXFCe/s7neivgPOr4PrWWUUow= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/collectd v0.124.1 h1:FYYg6t74WLfu7XJUxsuptkBi5f09FagFFfWXtZ7tNxk= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/collectd v0.124.1/go.mod h1:cYJVZNWz7IR9Pqv1MSEjvXWK4XVKKb8OrJBXp7igc4o= github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.124.1 h1:cFu3VtuWdwvD1ftvTXH3v4Rm+be3BBhCz1OwiKoUQN0= github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.124.1/go.mod h1:LtzoVivtOUNmNS+4xQjvuh7m6gbLJdmsyL/C+3/wtV4= github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.124.0 h1:gJ2gWuy+1kybCTQMLAxKTbcsIKKr3R8UDZhIagAey0k= @@ -1250,6 +1253,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0. github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.124.1/go.mod h1:NghU9p1ScHVYdzmSixmZjmPr9nWLk073kZugpQ1kdxw= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils v0.124.1 h1:E1e96GTHmiAfIfeYfA5ZVnOxud3+vbisGp0gE1tfd4s= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils v0.124.1/go.mod h1:MOhFATtYSLad9nKunjh6uGf8nQUcWje2LPlhD2uu3do= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata v0.124.1 h1:Fa3gk9Fd4b0rHn4U6rHe0gLEfV350oMoKAZSmp1Fzqc= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata v0.124.1/go.mod h1:+6G1ZQbG6JaKplqIWRHvL3XC2x2sR5xUW+LWjnolBLc= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.124.1 h1:QX88VjAiUaG7SQe4A14b3JPgvzZK6OXpWoBd32Zlp1o= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.124.1/go.mod h1:jRsmTRbn1HkrOpoeXS0KGfXobrh7E3W/p9fLwLdpcB8= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.124.1 h1:oiLa2lg+Ix+wHi4bPsnQ5+DJc6u+OHZt6YhP/rjgbNc= @@ -1286,6 +1291,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattribute github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor v0.124.1/go.mod h1:iWtC4dNuH2BxWctFmf4Vo/ZZsPvOjXqT8Lgh1TuR2JQ= github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricsgenerationprocessor v0.124.1 h1:uF82CnHnY2JVVTYOQ3la0tsGVc9qGeRKcL1hq963OcI= github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricsgenerationprocessor v0.124.1/go.mod h1:rT4Laop9cDWRmFFqm+bANV0l+CynIHZwNkWrOHAx1QE= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstarttimeprocessor v0.124.1 h1:7odWWas9WEkxsoyl5r6pcnrAuI73IWQcI8vsD9VV73M= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstarttimeprocessor v0.124.1/go.mod h1:7tbIyl89GmSFdASTocVruze2AXu1hQwGc2BhgHG26C8= github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstransformprocessor v0.124.1 h1:I+2/d/Chom9IRygTORTP6WbRNL4IYce8h7t9po1MExY= github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstransformprocessor v0.124.1/go.mod h1:M0MPNNbQ3Rt4b6MqaPe1TfBEP623LcELUMraOP0wNr4= github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.124.1 h1:AhU6499A/KYpwBMZDQUppJMwYfa4X1HUo77zOLvZ9jg= @@ -1300,12 +1307,18 @@ github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformpro github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.124.1/go.mod h1:1RwbGuCq0T5Tr+irJVm5PSJZCMS1n1J1Qs8DD4kJkS8= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsecscontainermetricsreceiver v0.124.1 h1:wlBB3waaBvWG+Vd2O/QBTheyjBVq4reaUmG1CbIX7WY= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsecscontainermetricsreceiver v0.124.1/go.mod h1:OScCy+A3O9xcfDfVbjyp/13ICDAv3vHzRao+2Cq+hI0= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver v0.124.1 h1:hbaqY9LPRqHZwM1U4Z2Rv7OEIWSpHtwmG3rJl9YHfMk= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver v0.124.1/go.mod h1:OtFzeuX5XqAMu+X7idShKBk0qp/9PjFF8CTm/1HBVms= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.124.1 h1:LPKySGw0KuHLsPH6N1fp1zx6Lkt4+wTY4sL7F1toBqU= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.124.1/go.mod h1:ztXVcqH5q0+o8RoCag+kZnCyUFvyeeHdaiMHegxaDc0= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.124.1 h1:s5IthsHQy1rJ7hnxGjPmzdJCppFqqbd2loIhmOWLS2k= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.124.1/go.mod h1:3MmO+ZJLcTPyYUk8tENgen4dnmiodTfQSHokwlvNZ+A= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.124.1 h1:r20zOMMBcxzI2Sni2HyTR7TVxuNXLn1Ov6qtnlVWoJc= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver v0.124.1/go.mod h1:1UsKa4xSNodA2i6ic5mmyd31khXyTGxsVKJoKmq/jtU= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.124.1 h1:Pdlf5D5gB/5Khi3t5PiojQrCulcEejecsxoSpOd0ekA= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver v0.124.1/go.mod h1:pKNXK34B2dV0nEDAFgHtYQHBlZpQlLWcKFZpblY3xQs= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.124.1 h1:/dcNbFj4cFZRXDmyBYTFT6+lclunY9slInimPIHqWj0= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.124.1/go.mod h1:+1JVHE45uoo4b5UnIZvHvotKtMUOXhdCdL6NlvrZ8Nk= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver v0.124.1 h1:Ag8P7xYAytcZd3/8RbUxUjcXcoc3vAQ0kTM8POiAWjc= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver v0.124.1/go.mod h1:m+AC6vDhk0ApXvvPZJCCiCaRlE1ua9hh2FHJdsAfAeI= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tcplogreceiver v0.124.1 h1:FraAggwubCbTbpBiuIIXxtzcmnUt8Zd1QT97Ay31TZw= @@ -1530,6 +1543,8 @@ github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= github.com/tidwall/wal v1.1.8 h1:2qDSGdAdjaY3PEvHRva+9UFqgk+ef7cOiW1Qn5JH1y0= github.com/tidwall/wal v1.1.8/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= +github.com/tilinna/clock v1.1.0 h1:6IQQQCo6KoBxVudv6gwtY8o4eDfhHo8ojA5dP0MfhSs= +github.com/tilinna/clock v1.1.0/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao= github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= @@ -1696,6 +1711,8 @@ go.opentelemetry.io/collector/exporter/exportertest v0.124.0 h1:IOxA/4CiVWGPlmA0 go.opentelemetry.io/collector/exporter/exportertest v0.124.0/go.mod h1:2EmU8IwVJV79MmFBFFW1LCN0Ob2UZsEkX/mSUB06lbI= go.opentelemetry.io/collector/exporter/nopexporter v0.124.0 h1:oGXHe7lqa1FebpbLL9S4njO86CUTLFDjDhLo18IkYQo= go.opentelemetry.io/collector/exporter/nopexporter v0.124.0/go.mod h1:Ceru8F6fsFoSieHJ3g9TLJMSICMK/KCmCqmAx15Hu/A= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.124.0 h1:047D4wLOb5ug4O99y3AjR/SNFTxlq3RAr2zr/8G1JWI= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.124.0/go.mod h1:Jk/1hiZvwy4dzFEfX37h18n3U9pNIfHSApcLjZmSFmU= go.opentelemetry.io/collector/exporter/xexporter v0.124.0 h1:Itfn2+F4ki8hObOtPCecWBwGpuxakUYSsTwwkB5iUns= go.opentelemetry.io/collector/exporter/xexporter v0.124.0/go.mod h1:dNK/PPY02gA9BawIKHyVk8kIFdYvqVZ2A+LlMZucIPY= go.opentelemetry.io/collector/extension v1.30.0 h1:AJqntAp1p40Q1az2Vze3OHiMURq56KWnUxaLzs1ghaA= @@ -1770,6 +1787,8 @@ go.opentelemetry.io/collector/scraper v0.124.0 h1:l+nJdtcTDQX59oMqeV8mD9hjOe62/N go.opentelemetry.io/collector/scraper v0.124.0/go.mod h1:NNIt4NUIS3LftpIng8h27APi7NYqw0EkfMoQ9rqlFZg= go.opentelemetry.io/collector/scraper/scraperhelper v0.124.0 h1:sDvdoL8CXzzNG3TUeg3940dwhEuqpaF9JoU+55Gsr84= go.opentelemetry.io/collector/scraper/scraperhelper v0.124.0/go.mod h1:WCDLFM7iIbk7e0s1pCum31yXBSFN+U9uY+0N6ODMLXI= +go.opentelemetry.io/collector/scraper/scrapertest v0.124.0 h1:ZaA7vDnFQOboNJPBtUF716izHACHJX25zfnHvyLhphY= +go.opentelemetry.io/collector/scraper/scrapertest v0.124.0/go.mod h1:cii9PveCCRao5+hsObc8F60e4A22qEwsjZd4Umy6siQ= go.opentelemetry.io/collector/semconv v0.124.0 h1:YTdo3UFwNyDQCh9DiSm2rbzAgBuwn/9dNZ0rv454goA= go.opentelemetry.io/collector/semconv v0.124.0/go.mod h1:te6VQ4zZJO5Lp8dM2XIhDxDiL45mwX0YAQQWRQ0Qr9U= go.opentelemetry.io/collector/service v0.124.0 h1:lUpizko/Y2P+XXbZ9wiKM8acLSt6ZIvC3/6/j6rcq4w= @@ -2382,8 +2401,8 @@ k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/kubelet v0.30.0 h1:/pqHVR2Rn8ExCpn211wL3pMtqRFpcBcJPl4+1INbIMk= -k8s.io/kubelet v0.30.0/go.mod h1:WukdKqbQxnj+csn3K8XOKeX7Sh60J/da25IILjvvB5s= +k8s.io/kubelet v0.32.3 h1:B9HzW4yB67flx8tN2FYuDwZvxnmK3v5EjxxFvOYjmc8= +k8s.io/kubelet v0.32.3/go.mod h1:yyAQSCKC+tjSlaFw4HQG7Jein+vo+GeKBGdXdQGvL1U= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/internal/merge/confmap/load.go b/internal/merge/confmap/load.go index c6965e72311..c983e5638db 100644 --- a/internal/merge/confmap/load.go +++ b/internal/merge/confmap/load.go @@ -13,6 +13,7 @@ import ( type Loader interface { Load() (*Conf, error) + ID() string } type FileLoader struct { @@ -23,6 +24,10 @@ func NewFileLoader(path string) *FileLoader { return &FileLoader{path: path} } +func (f *FileLoader) ID() string { + return f.path +} + func (f *FileLoader) Load() (*Conf, error) { // Clean the path before using it. content, err := os.ReadFile(filepath.Clean(f.path)) @@ -41,6 +46,10 @@ func NewByteLoader(id string, content []byte) *ByteLoader { return &ByteLoader{id: id, content: content} } +func (b *ByteLoader) ID() string { + return b.id +} + func (b *ByteLoader) Load() (*Conf, error) { var rawConf map[string]any if err := yaml.Unmarshal(b.content, &rawConf); err != nil { diff --git a/internal/merge/confmap/load_test.go b/internal/merge/confmap/load_test.go index df9b7735395..33759cf4bee 100644 --- a/internal/merge/confmap/load_test.go +++ b/internal/merge/confmap/load_test.go @@ -12,6 +12,7 @@ import ( func TestFileLoader(t *testing.T) { loader := NewFileLoader(filepath.Join("not", "a", "file")) + assert.Equal(t, filepath.Join("not", "a", "file"), loader.ID()) got, err := loader.Load() assert.Error(t, err) assert.Nil(t, got) @@ -26,6 +27,7 @@ func TestByteLoader(t *testing.T) { nop/1: ` loader := NewByteLoader("invalid-yaml", []byte("string")) + assert.Equal(t, "invalid-yaml", loader.ID()) got, err := loader.Load() assert.Error(t, err) assert.Nil(t, got) diff --git a/service/configprovider/otlphttp_validator.go b/service/configprovider/otlphttp_validator.go new file mode 100644 index 00000000000..fb6c24110e1 --- /dev/null +++ b/service/configprovider/otlphttp_validator.go @@ -0,0 +1,88 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package configprovider + +import ( + "context" + "fmt" + "net/url" + "strings" + + "github.com/aws/aws-sdk-go/aws/endpoints" + "go.opentelemetry.io/collector/confmap" +) + +// allowedDNSSuffixes contains AWS partition DNS suffixes (e.g., amazonaws.com, api.aws). +var allowedDNSSuffixes = buildAllowedDNSSuffixes() + +func buildAllowedDNSSuffixes() []string { + var suffixes []string + for _, p := range endpoints.DefaultPartitions() { + suffixes = append(suffixes, p.DNSSuffix()) + } + suffixes = append(suffixes, "api.aws") + return suffixes +} + +// otlphttpValidator is a confmap.Converter that validates otlphttp exporter endpoints +// are restricted to AWS endpoints only. +type otlphttpValidator struct{} + +// NewOTLPHTTPValidatorFactory returns a factory for creating otlphttp endpoint validators. +func NewOTLPHTTPValidatorFactory() confmap.ConverterFactory { + return confmap.NewConverterFactory(func(_ confmap.ConverterSettings) confmap.Converter { + return &otlphttpValidator{} + }) +} + +// Convert validates that all otlphttp exporters use only allowed AWS endpoints. +func (v *otlphttpValidator) Convert(_ context.Context, conf *confmap.Conf) error { + exportersVal := conf.Get("exporters") + if exportersVal == nil { + return nil + } + exporters, ok := exportersVal.(map[string]any) + if !ok { + return nil + } + for name, cfg := range exporters { + if name != "otlphttp" && !strings.HasPrefix(name, "otlphttp/") { + continue + } + exporterCfg, ok := cfg.(map[string]any) + if !ok { + continue + } + for _, key := range []string{"endpoint", "metrics_endpoint", "traces_endpoint", "logs_endpoint"} { + if ep, ok := exporterCfg[key].(string); ok && ep != "" { + if !isAWSEndpoint(ep) { + return fmt.Errorf("invalid AWS endpoint: %q", ep) + } + } + } + } + return nil +} + +// isAWSEndpoint checks if the endpoint host ends with an AWS DNS suffix. +func isAWSEndpoint(endpoint string) bool { + // If endpoint doesn't contain a scheme, add https:// as default to allow for validation of domain name + if !strings.Contains(endpoint, "://") { + endpoint = "https://" + endpoint + } + u, err := url.Parse(endpoint) + if err != nil { + return false + } + host := strings.ToLower(u.Hostname()) + if host == "" { + return false + } + for _, suffix := range allowedDNSSuffixes { + if strings.HasSuffix(host, "."+suffix) { + return true + } + } + return false +} diff --git a/service/configprovider/otlphttp_validator_test.go b/service/configprovider/otlphttp_validator_test.go new file mode 100644 index 00000000000..a74ccf9e6c9 --- /dev/null +++ b/service/configprovider/otlphttp_validator_test.go @@ -0,0 +1,170 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package configprovider + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/confmap" +) + +func TestOTLPHTTPValidator(t *testing.T) { + tests := []struct { + name string + config map[string]any + wantErr bool + }{ + { + name: "valid amazonaws.com endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "endpoint": "https://monitoring.us-east-1.amazonaws.com", + }, + }, + }, + }, + { + name: "valid api.aws endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "endpoint": "https://monitoring.us-east-1.api.aws", + }, + }, + }, + }, + { + name: "valid metrics_endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "metrics_endpoint": "https://monitoring.us-east-1.amazonaws.com/v1/metrics", + }, + }, + }, + }, + { + name: "valid traces_endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "traces_endpoint": "https://xray.us-west-2.amazonaws.com/v1/traces", + }, + }, + }, + }, + { + name: "valid logs_endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "logs_endpoint": "https://logs.eu-west-1.amazonaws.com/v1/logs", + }, + }, + }, + }, + { + name: "valid endpoint without scheme", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "endpoint": "xray.us-west-2.amazonaws.com", + }, + }, + }, + }, + { + name: "valid china partition", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "endpoint": "https://monitoring.cn-north-1.amazonaws.com.cn", + }, + }, + }, + }, + { + name: "invalid third party endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "endpoint": "https://example.com/v1/metrics", + }, + }, + }, + wantErr: true, + }, + { + name: "invalid third party metrics_endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "metrics_endpoint": "https://otel-collector.example.com/v1/metrics", + }, + }, + }, + wantErr: true, + }, + { + name: "invalid third party traces_endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "traces_endpoint": "https://jaeger.example.com/v1/traces", + }, + }, + }, + wantErr: true, + }, + { + name: "invalid third party logs_endpoint", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "logs_endpoint": "https://splunk.example.com/v1/logs", + }, + }, + }, + wantErr: true, + }, + { + name: "non-otlphttp exporter is ignored", + config: map[string]any{ + "exporters": map[string]any{ + "prometheus": map[string]any{ + "endpoint": "https://example.com/metrics", + }, + }, + }, + }, + { + name: "invalid spoofed suffix", + config: map[string]any{ + "exporters": map[string]any{ + "otlphttp": map[string]any{ + "endpoint": "https://evil-amazonaws.com", + }, + }, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + validator := NewOTLPHTTPValidatorFactory().Create(confmap.ConverterSettings{}) + err := validator.Convert(context.Background(), confmap.NewFromStringMap(tt.config)) + if tt.wantErr { + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid AWS endpoint") + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/service/configprovider/provider.go b/service/configprovider/provider.go index de5b0767b9f..42b8eebc5e0 100644 --- a/service/configprovider/provider.go +++ b/service/configprovider/provider.go @@ -20,9 +20,12 @@ func GetSettings(uris []string, logger *zap.Logger) otelcol.ConfigProviderSettin fileprovider.NewFactory(), envprovider.NewFactory(), }, - ProviderSettings: confmap.ProviderSettings{Logger: logger}, - ConverterFactories: []confmap.ConverterFactory{expandconverter.NewFactory()}, - ConverterSettings: confmap.ConverterSettings{Logger: logger}, + ProviderSettings: confmap.ProviderSettings{Logger: logger}, + ConverterFactories: []confmap.ConverterFactory{ + expandconverter.NewFactory(), //nolint:staticcheck // TODO: breaks logging if updated, migrate later + NewOTLPHTTPValidatorFactory(), + }, + ConverterSettings: confmap.ConverterSettings{Logger: logger}, }, } return settings diff --git a/service/defaultcomponents/components.go b/service/defaultcomponents/components.go index 24443d37cbe..051df563ebe 100644 --- a/service/defaultcomponents/components.go +++ b/service/defaultcomponents/components.go @@ -23,6 +23,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/processor/groupbytraceprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricsgenerationprocessor" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstarttimeprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/metricstransformprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor" @@ -34,10 +35,13 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightskueuereceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsecscontainermetricsreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tcplogreceiver" @@ -46,6 +50,7 @@ import ( "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/debugexporter" "go.opentelemetry.io/collector/exporter/nopexporter" + "go.opentelemetry.io/collector/exporter/otlphttpexporter" "go.opentelemetry.io/collector/extension" "go.opentelemetry.io/collector/extension/zpagesextension" "go.opentelemetry.io/collector/otelcol" @@ -82,10 +87,13 @@ func Factories() (otelcol.Factories, error) { awsecscontainermetricsreceiver.NewFactory(), awsnvmereceiver.NewFactory(), awsxrayreceiver.NewFactory(), + collectdreceiver.NewFactory(), filelogreceiver.NewFactory(), + hostmetricsreceiver.NewFactory(), jaegerreceiver.NewFactory(), jmxreceiver.NewFactory(), kafkareceiver.NewFactory(), + kubeletstatsreceiver.NewFactory(), nopreceiver.NewFactory(), otlpreceiver.NewFactory(), prometheusreceiver.NewFactory(), @@ -116,6 +124,7 @@ func Factories() (otelcol.Factories, error) { k8sattributesprocessor.NewFactory(), memorylimiterprocessor.NewFactory(), metricsgenerationprocessor.NewFactory(), + metricstarttimeprocessor.NewFactory(), metricstransformprocessor.NewFactory(), probabilisticsamplerprocessor.NewFactory(), resourceprocessor.NewFactory(), @@ -135,6 +144,7 @@ func Factories() (otelcol.Factories, error) { cloudwatch.NewFactory(), debugexporter.NewFactory(), nopexporter.NewFactory(), + otlphttpexporter.NewFactory(), prometheusremotewriteexporter.NewFactory(), ); err != nil { return otelcol.Factories{}, err diff --git a/service/defaultcomponents/components_test.go b/service/defaultcomponents/components_test.go index 8e9868d939c..ba76775943f 100644 --- a/service/defaultcomponents/components_test.go +++ b/service/defaultcomponents/components_test.go @@ -22,10 +22,13 @@ func TestComponents(t *testing.T) { "awsecscontainermetrics", "awsnvmereceiver", "awsxray", + "collectd", "filelog", + "hostmetrics", "jaeger", "jmx", "kafka", + "kubeletstats", "nop", "otlp", "prometheus", @@ -52,6 +55,7 @@ func TestComponents(t *testing.T) { "deltatorate", "ec2tagger", "metricsgeneration", + "metricstarttime", "filter", "gpuattributes", "kueueattributes", @@ -81,6 +85,7 @@ func TestComponents(t *testing.T) { "awsxray", "debug", "nop", + "otlphttp", "prometheusremotewrite", } gotExporters := collections.MapSlice(maps.Keys(factories.Exporters), component.Type.String) diff --git a/tool/translator/translator.go b/tool/translator/translator.go index a4e5c0c3cd0..e2b3559852b 100644 --- a/tool/translator/translator.go +++ b/tool/translator/translator.go @@ -23,6 +23,7 @@ import ( const ( exitErrorMessage = "configuration validation first phase failed. Agent version: %v. Verify the JSON input is only using features supported by this version" exitSuccessMessage = "Configuration validation first phase succeeded" + exitSkipMessage = "Configuration validation first phase skipped. No JSON files found" version = "1.0" envConfigFileName = "env-config.json" yamlConfigFileName = "amazon-cloudwatch-agent.yaml" @@ -98,12 +99,23 @@ func (ct *ConfigTranslator) Translate() (err error) { } }() + tomlConfigPath := cmdutil.GetTomlConfigPath(ct.ctx.OutputTomlFilePath()) + tomlConfigDir := filepath.Dir(tomlConfigPath) + yamlConfigPath := filepath.Join(tomlConfigDir, yamlConfigFileName) + mergedJSONConfigMap, err := cmdutil.GenerateMergedJsonConfigMap(ct.ctx) - if err != nil { + onlyYAML := errors.Is(err, cmdutil.ErrOnlyYAML) + if err != nil && !onlyYAML { return fmt.Errorf("failed to generate merged json config: %v", err) } + if onlyYAML { + log.Println(exitSkipMessage) + // YAML-only configs still require a generated TOML for agent and logging configuration. + // TOML translation requires a non-nil map. + mergedJSONConfigMap = map[string]any{} + } - if !ct.ctx.RunInContainer() { + if !onlyYAML && !ct.ctx.RunInContainer() { current, err := user.Current() if err == nil && current.Name == "****" { runAsUser, err := userutil.DetectRunAsUser(mergedJSONConfigMap) @@ -114,24 +126,30 @@ func (ct *ConfigTranslator) Translate() (err error) { } } - tomlConfigPath := cmdutil.GetTomlConfigPath(ct.ctx.OutputTomlFilePath()) - tomlConfigDir := filepath.Dir(tomlConfigPath) - yamlConfigPath := filepath.Join(tomlConfigDir, yamlConfigFileName) tomlConfig, err := cmdutil.TranslateJsonMapToTomlConfig(mergedJSONConfigMap) if err != nil { return fmt.Errorf("failed to generate TOML configuration validation content: %v", err) } - yamlConfig, err := cmdutil.TranslateJsonMapToYamlConfig(mergedJSONConfigMap) - if err != nil && !errors.Is(err, pipeline.ErrNoPipelines) { - return fmt.Errorf("failed to generate YAML configuration validation content: %v", err) + var yamlConfig any + if !onlyYAML { + yamlConfig, err = cmdutil.TranslateJsonMapToYamlConfig(mergedJSONConfigMap) + if err != nil && !errors.Is(err, pipeline.ErrNoPipelines) { + return fmt.Errorf("failed to generate YAML configuration validation content: %v", err) + } } if err = cmdutil.ConfigToTomlFile(tomlConfig, tomlConfigPath); err != nil { return fmt.Errorf("failed to create the configuration TOML validation file: %v", err) } - if err = cmdutil.ConfigToYamlFile(yamlConfig, yamlConfigPath); err != nil { - return fmt.Errorf("failed to create the configuration YAML validation file: %v", err) + if yamlConfig != nil { + if err = cmdutil.ConfigToYamlFile(yamlConfig, yamlConfigPath); err != nil { + return fmt.Errorf("failed to create the configuration YAML validation file: %v", err) + } + } else { + _ = os.Remove(yamlConfigPath) + } + if !onlyYAML { + log.Println(exitSuccessMessage) } - log.Println(exitSuccessMessage) envConfigPath := filepath.Join(tomlConfigDir, envConfigFileName) cmdutil.TranslateJsonMapToEnvConfigFile(mergedJSONConfigMap, envConfigPath) diff --git a/tool/translator/translator_test.go b/tool/translator/translator_test.go new file mode 100644 index 00000000000..ec066e3d8e4 --- /dev/null +++ b/tool/translator/translator_test.go @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package translator + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aws/amazon-cloudwatch-agent/translator" + translatorcontext "github.com/aws/amazon-cloudwatch-agent/translator/context" + translatorutil "github.com/aws/amazon-cloudwatch-agent/translator/util" +) + +func TestTranslate_OnlyYAML(t *testing.T) { + orig := translatorutil.DetectRegion + translatorutil.DetectRegion = func(string, map[string]string) (string, string) { + return "us-east-1", "mock" + } + defer func() { translatorutil.DetectRegion = orig }() + + translator.ResetMessages() + translatorcontext.ResetContext() + + tmpDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "config.yaml"), nil, 0600)) + + tomlPath := filepath.Join(tmpDir, "output.toml") + // Pre-create the YAML output file to verify it gets removed. + yamlPath := filepath.Join(tmpDir, yamlConfigFileName) + require.NoError(t, os.WriteFile(yamlPath, nil, 0600)) + + ct, err := NewConfigTranslator("linux", "", tmpDir, tomlPath, "ec2", "", "default") + require.NoError(t, err) + + assert.NoError(t, ct.Translate()) + assert.FileExists(t, tomlPath) + assert.NoFileExists(t, yamlPath) +} diff --git a/translator/cmdutil/translatorutil.go b/translator/cmdutil/translatorutil.go index 167fe7e8e03..883015d8d13 100644 --- a/translator/cmdutil/translatorutil.go +++ b/translator/cmdutil/translatorutil.go @@ -4,6 +4,7 @@ package cmdutil import ( + "errors" "fmt" "log" "os" @@ -36,6 +37,8 @@ const ( defaultTomlConfigName = "CWAgent.conf" ) +var ErrOnlyYAML = errors.New("only YAML files detected") + // TranslateJsonMapToEnvConfigFile populates env-config.json based on the input json config. func TranslateJsonMapToEnvConfigFile(jsonConfigValue map[string]interface{}, envConfigPath string) { if envConfigPath == "" { @@ -115,6 +118,7 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, // for the append operation when the existing file name and new .tmp file name have diff // only for the ".tmp" suffix, i.e. it is override operation even it says append. var jsonConfigMapMap = make(map[string]map[string]interface{}) + var foundYAML bool if ctx.MultiConfig() == "append" || ctx.MultiConfig() == "remove" { // backwards compatible for the old json config file @@ -163,6 +167,7 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, ext = filepath.Ext(key) // skip .yaml files if ext == constants.FileSuffixYAML { + foundYAML = true return nil } if ctx.MultiConfig() == "default" || ctx.MultiConfig() == "append" { @@ -176,6 +181,7 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, } } else if ext == constants.FileSuffixYAML { // skip .yaml files + foundYAML = true return nil } else { // non .tmp / existing files @@ -208,6 +214,9 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, } jsonConfigMapMap[config.CWConfigContent] = jm } + if foundYAML && len(jsonConfigMapMap) == 0 { + return nil, ErrOnlyYAML + } } defaultConfig, err := translatorUtil.GetDefaultJsonConfigMap(ctx.Os(), ctx.Mode()) diff --git a/translator/cmdutil/translatorutil_test.go b/translator/cmdutil/translatorutil_test.go index 2afa618b5e2..bf78a5cf1e9 100644 --- a/translator/cmdutil/translatorutil_test.go +++ b/translator/cmdutil/translatorutil_test.go @@ -6,13 +6,15 @@ package cmdutil import ( "encoding/json" "os" - "path" + "path/filepath" "regexp" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" + translatorcontext "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/util" ) @@ -24,7 +26,7 @@ func TestTranslateJsonMapToEnvConfigFile(t *testing.T) { "aws_sdk_log_level": "loglevel", }, } - envConfigPath := path.Join(t.TempDir(), "env-config.json") + envConfigPath := filepath.Join(t.TempDir(), "env-config.json") expectedFile := "testdata/env-config.json" TranslateJsonMapToEnvConfigFile(jsonConfigValue, envConfigPath) @@ -237,3 +239,80 @@ func checkIfSchemaValidateAsExpected(t *testing.T, jsonInputPath string, shouldS assert.False(t, shouldSuccess, "It should pass the schemaValidation!") } } + +func TestGenerateMergedJsonConfigMap_OnlyYAML(t *testing.T) { + testCases := map[string]string{ + "WithYAMLFile": "config.yaml", + "WithYAMLTmpFile": "config.yaml.tmp", + } + for name, file := range testCases { + t.Run(name, func(t *testing.T) { + tmpDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, file), nil, 0600)) + + translatorcontext.ResetContext() + ctx := translatorcontext.CurrentContext() + ctx.SetInputJsonDirPath(tmpDir) + ctx.SetMultiConfig("default") + + got, err := GenerateMergedJsonConfigMap(ctx) + + assert.ErrorIs(t, err, ErrOnlyYAML) + assert.Nil(t, got) + }) + } +} + +func TestGenerateMergedJsonConfigMap_MixedJSONAndYAML(t *testing.T) { + for _, mode := range []string{"append", "remove"} { + t.Run(mode, func(t *testing.T) { + tmpDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "config.json"), []byte(`{"agent":{"debug":true}}`), 0600)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "otel.yaml"), nil, 0600)) + + translatorcontext.ResetContext() + ctx := translatorcontext.CurrentContext() + ctx.SetInputJsonDirPath(tmpDir) + ctx.SetMultiConfig(mode) + + result, err := GenerateMergedJsonConfigMap(ctx) + + assert.NoError(t, err) + assert.NotNil(t, result) + }) + } +} + +func TestGenerateMergedJsonConfigMap_DefaultModeWithTmpJSONAndYAML(t *testing.T) { + tmpDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "config.json.tmp"), []byte(`{"agent":{"debug":true}}`), 0600)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "otel.yaml"), nil, 0600)) + + translatorcontext.ResetContext() + ctx := translatorcontext.CurrentContext() + ctx.SetInputJsonDirPath(tmpDir) + ctx.SetMultiConfig("default") + + result, err := GenerateMergedJsonConfigMap(ctx) + + assert.NoError(t, err) + assert.NotNil(t, result) +} + +func TestGenerateMergedJsonConfigMap_EnvVarJSONWithYAML(t *testing.T) { + tmpDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "otel.yaml"), nil, 0600)) + + t.Setenv(envconfig.RunInContainer, envconfig.TrueValue) + t.Setenv(envconfig.CWConfigContent, `{"agent":{"debug":true}}`) + + translatorcontext.ResetContext() + ctx := translatorcontext.CurrentContext() + ctx.SetInputJsonDirPath(tmpDir) + ctx.SetMultiConfig("default") + + result, err := GenerateMergedJsonConfigMap(ctx) + + assert.NoError(t, err) + assert.NotNil(t, result) +}