Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions translator/tocwconfig/tocwconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,51 @@ func verifyToYamlTranslation(t *testing.T, input interface{}, expectedYamlFilePa
yamlStr := toyamlconfig.ToYamlConfig(yamlConfig)
require.NoError(t, yaml.Unmarshal([]byte(yamlStr), &actual))

// Add health check extension to expected results for Kubernetes environments

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I agree with this approach to add the health extension for the sake of unit tests. I would have updated all the expected yaml for kubernetes to expect the health extension.

Although I am open to disagreeing to my proposal since this is primarily for fixing unit tests

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially thought I wasn't supposed to edit the YAML files, but I agree with the proposal and have updated the expected Kubernetes YAML to include the health extension.

// The health check extension is automatically added for Kubernetes environments in the translator
if context.CurrentContext().KubernetesMode() != "" {
if expectedMap, ok := expected.(map[string]interface{}); ok {
// Ensure extensions map exists
if _, exists := expectedMap["extensions"]; !exists {
expectedMap["extensions"] = make(map[string]interface{})
}
if extMap, ok := expectedMap["extensions"].(map[string]interface{}); ok {
// Add health check extension if it doesn't exist
if _, exists := extMap["health_check/health_check"]; !exists {
extMap["health_check/health_check"] = map[string]interface{}{
"endpoint": "0.0.0.0:13133",
"path": "/",
}
}
}
// Ensure service extensions list includes health check
if service, exists := expectedMap["service"]; exists {
if serviceMap, ok := service.(map[string]interface{}); ok {
if serviceExtensions, exists := serviceMap["extensions"]; exists {
if extSlice, ok := serviceExtensions.([]interface{}); ok {
// Check if health_check/health_check is already in the list
hasHealthCheck := false
for _, ext := range extSlice {
if extStr, ok := ext.(string); ok && extStr == "health_check/health_check" {
hasHealthCheck = true
break
}
}
// Add it if it's missing
if !hasHealthCheck {
extSlice = append(extSlice, "health_check/health_check")
serviceMap["extensions"] = extSlice
}
}
} else {
// Create extensions list with health check if it doesn't exist
serviceMap["extensions"] = []interface{}{"health_check/health_check"}
}
}
}
}
}

//assert.NoError(t, os.WriteFile(expectedYamlFilePath, []byte(yamlStr), 0644)) // useful for regenerating YAML
opt := cmpopts.SortSlices(func(x, y interface{}) bool {
return pretty.Sprint(x) < pretty.Sprint(y)
Expand Down
37 changes: 37 additions & 0 deletions translator/translate/otel/extension/healthcheck/translator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package healthcheck

import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"

"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common"
)

type healthCheckTranslator struct {
name string
}

var _ common.Translator[component.Config, component.ID] = (*healthCheckTranslator)(nil)

func NewHealthCheckTranslator() common.Translator[component.Config, component.ID] {
return &healthCheckTranslator{name: "health_check"}
}

func (t *healthCheckTranslator) ID() component.ID {
return component.NewIDWithName(component.MustNewType("health_check"), t.name)
}

func (t *healthCheckTranslator) Translate(_ *confmap.Conf) (component.Config, error) {
cfg := &struct {
Endpoint string `mapstructure:"endpoint"`
Path string `mapstructure:"path"`
}{
Endpoint: "0.0.0.0:13133",
Path: "/",
}

return cfg, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package healthcheck

import (
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/confmap"
)

func TestHealthCheckTranslator(t *testing.T) {
translator := NewHealthCheckTranslator()
assert.Equal(t, "health_check", translator.ID().Type().String())

conf := confmap.New()
cfg, err := translator.Translate(conf)
assert.NoError(t, err)

// Assert the config has the expected fields
healthCheckCfg, ok := cfg.(*struct {
Endpoint string `mapstructure:"endpoint"`
Path string `mapstructure:"path"`
})
assert.True(t, ok)
assert.Equal(t, "0.0.0.0:13133", healthCheckCfg.Endpoint)
assert.Equal(t, "/", healthCheckCfg.Path)
}
2 changes: 2 additions & 0 deletions translator/translate/otel/translate_otel.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/aws/amazon-cloudwatch-agent/translator/context"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/entitystore"
healthcheckextension "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/healthcheck"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/extension/server"
pipelinetranslator "github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline/applicationsignals"
Expand Down Expand Up @@ -92,6 +93,7 @@ func Translate(jsonConfig interface{}, os string) (*otelcol.Config, error) {
}
if context.CurrentContext().KubernetesMode() != "" {
pipelines.Translators.Extensions.Set(server.NewTranslator())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add pipelines.Translators.Extensions.Set(healthcheckextension.NewHealthCheckTranslator()) under here since we only need the healthcheck extension for Kubernetes environments, not just when the agent is in a container.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I moved it under the Kubernetes mode check since we only need the health check extension for Kubernetes environments.

pipelines.Translators.Extensions.Set(healthcheckextension.NewHealthCheckTranslator())
}

cfg := &otelcol.Config{
Expand Down
58 changes: 58 additions & 0 deletions translator/translate/otel/translate_otel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,71 @@ import (

"github.com/aws/amazon-cloudwatch-agent/tool/testutil"
"github.com/aws/amazon-cloudwatch-agent/translator"
"github.com/aws/amazon-cloudwatch-agent/translator/context"
_ "github.com/aws/amazon-cloudwatch-agent/translator/registerrules"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/agent"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common"
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/pipeline/prometheus"
"github.com/aws/amazon-cloudwatch-agent/translator/util/eksdetector"
)

func TestHealthCheckExtension(t *testing.T) {
agent.Global_Config.Region = "us-east-1"

// Test case 1: Non-Kubernetes environment should NOT have health check extension
input := map[string]interface{}{
"metrics": map[string]interface{}{
"metrics_collected": map[string]interface{}{
"cpu": map[string]interface{}{},
},
},
}

cfg, err := Translate(input, "linux")
require.NoError(t, err)
require.NotNil(t, cfg)

// Verify that the health check extension is NOT registered for non-Kubernetes
extensionFound := false
for _, ext := range cfg.Service.Extensions {
if ext.Type().String() == "health_check" {
extensionFound = true
break
}
}
assert.False(t, extensionFound, "Health check extension should NOT be registered for non-Kubernetes environments")

// Test case 2: Kubernetes environment should have health check extension
inputK8s := map[string]interface{}{
"logs": map[string]interface{}{
"metrics_collected": map[string]interface{}{
"kubernetes": map[string]interface{}{
"cluster_name": "TestCluster",
},
},
},
}

// Set Kubernetes mode in context for this test
ctx := context.CurrentContext()
ctx.SetKubernetesMode("EKS")
defer ctx.SetKubernetesMode("") // Reset after test

cfgK8s, err := Translate(inputK8s, "linux")
require.NoError(t, err)
require.NotNil(t, cfgK8s)

// Verify that the health check extension IS registered for Kubernetes
extensionFoundK8s := false
for _, ext := range cfgK8s.Service.Extensions {
if ext.Type().String() == "health_check" {
extensionFoundK8s = true
break
}
}
assert.True(t, extensionFoundK8s, "Health check extension should be registered for Kubernetes environments")
}

func TestTranslator(t *testing.T) {
agent.Global_Config.Region = "us-east-1"
testutil.SetPrometheusRemoteWriteTestingEnv(t)
Expand Down