Skip to content

Commit

Permalink
[AppSignals] EC2 Support: awsappsignals processor updates (aws#669)
Browse files Browse the repository at this point in the history
- Added a number of variables analogous to the EKS, K8s, and generic use case ones to support the new schema for EC2
- Added specific logic to account for the different customer use cases regarding HostedIn.EC2.Environment, i.e. the use of customer input for this variable vs. the use of ASG value.
  • Loading branch information
majanjua-amzn authored and lisguo committed Feb 22, 2024
1 parent bbea252 commit e009c44
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 1 deletion.
4 changes: 4 additions & 0 deletions plugins/processors/awsappsignals/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func (cfg *Config) Validate() error {
if resolver.Name == "" {
return errors.New("name must not be empty for k8s resolver")
}
case PlatformEC2:
if resolver.Name == "" {
return errors.New("name must not be empty for ec2 resolver")
}
case PlatformGeneric:
default:
return errors.New("unknown resolver")
Expand Down
14 changes: 13 additions & 1 deletion plugins/processors/awsappsignals/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ func TestValidatePassed(t *testing.T) {
Rules: nil,
}
assert.Nil(t, config.Validate())

config = Config{
Resolvers: []Resolver{NewEC2Resolver("test"), NewGenericResolver("")},
Rules: nil,
}
assert.Nil(t, config.Validate())
}

func TestValidateFailedOnEmptyResolver(t *testing.T) {
Expand All @@ -31,7 +37,7 @@ func TestValidateFailedOnEmptyResolver(t *testing.T) {
assert.NotNil(t, config.Validate())
}

func TestValidateFailedOnEmptyClusterName(t *testing.T) {
func TestValidateFailedOnEmptyResolverName(t *testing.T) {
config := Config{
Resolvers: []Resolver{NewEKSResolver("")},
Rules: nil,
Expand All @@ -43,4 +49,10 @@ func TestValidateFailedOnEmptyClusterName(t *testing.T) {
Rules: nil,
}
assert.NotNil(t, config.Validate())

config = Config{
Resolvers: []Resolver{NewEC2Resolver("")},
Rules: nil,
}
assert.NotNil(t, config.Validate())
}
9 changes: 9 additions & 0 deletions plugins/processors/awsappsignals/config/resolvers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const (
PlatformEKS = "eks"
// PlatformK8s Native Kubernetes
PlatformK8s = "k8s"
// PlatformEC2 Amazon EC2 platform
PlatformEC2 = "ec2"
)

type Resolver struct {
Expand All @@ -31,6 +33,13 @@ func NewK8sResolver(name string) Resolver {
}
}

func NewEC2Resolver(name string) Resolver {
return Resolver{
Name: name,
Platform: PlatformEC2,
}
}

func NewGenericResolver(name string) Resolver {
return Resolver{
Name: name,
Expand Down
5 changes: 5 additions & 0 deletions plugins/processors/awsappsignals/config/resolvers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ func TestK8sResolver(t *testing.T) {
assert.Equal(t, "k8s", resolver.Platform)
}

func TestEC2Resolver(t *testing.T) {
resolver := NewEC2Resolver("test")
assert.Equal(t, "ec2", resolver.Platform)
}

func TestNewGenericResolver(t *testing.T) {
resolver := NewGenericResolver("")
assert.Equal(t, "generic", resolver.Platform)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ const (
K8SPodName = "k8s.pod.name"
K8SRemoteNamespace = "K8s.RemoteNamespace"

// ec2 resource attributes
EC2AutoScalingGroupName = "EC2.AutoScalingGroupName"

// hosted in attribute names
HostedInClusterNameEKS = "HostedIn.EKS.Cluster"
HostedInClusterNameK8s = "HostedIn.K8s.Cluster"
HostedInK8SNamespace = "HostedIn.K8s.Namespace"
HostedInEC2Environment = "HostedIn.EC2.Environment"
HostedInEnvironment = "HostedIn.Environment"
)
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func NewAttributesResolver(resolvers []appsignalsconfig.Resolver, logger *zap.Lo
for _, resolver := range resolvers {
if resolver.Platform == appsignalsconfig.PlatformEKS || resolver.Platform == appsignalsconfig.PlatformK8s {
subResolvers = append(subResolvers, getKubernetesResolver(logger), newKubernetesHostedInAttributeResolver(resolver.Name))
} else if resolver.Platform == appsignalsconfig.PlatformEC2 {
subResolvers = append(subResolvers, newEC2HostedInAttributeResolver(resolver.Name))
} else {
subResolvers = append(subResolvers, newHostedInAttributeResolver(resolver.Name, DefaultHostedInAttributes))
}
Expand Down
58 changes: 58 additions & 0 deletions plugins/processors/awsappsignals/internal/resolver/ec2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package resolver

import (
"context"

"go.opentelemetry.io/collector/pdata/pcommon"

attr "github.com/aws/amazon-cloudwatch-agent/plugins/processors/awsappsignals/internal/attributes"
)

const AttributePlatformEC2 = "EC2"

var EC2HostedInAttributes = map[string]string{
attr.AWSHostedInEnvironment: attr.HostedInEC2Environment,
}

type ec2HostedInAttributeResolver struct {
name string
attributeMap map[string]string
}

func newEC2HostedInAttributeResolver(name string) *ec2HostedInAttributeResolver {
if name == "" {
name = AttributePlatformEC2
}
return &ec2HostedInAttributeResolver{
name: name,
attributeMap: map[string]string{
attr.EC2AutoScalingGroupName: attr.HostedInEC2Environment,
},
}
}
func (h *ec2HostedInAttributeResolver) Process(attributes, resourceAttributes pcommon.Map) error {
for attrKey, mappingKey := range h.attributeMap {
if val, ok := resourceAttributes.Get(attrKey); ok {
attributes.PutStr(mappingKey, val.AsString())
}
}

// If aws.hostedin.environment is populated, override HostedIn.EC2.Environment value
// Otherwise, keep ASG name if it exists
if val, ok := resourceAttributes.Get(attr.AWSHostedInEnvironment); ok {
attributes.PutStr(attr.HostedInEC2Environment, val.AsString())
} else if val, ok := resourceAttributes.Get(attr.EC2AutoScalingGroupName); ok {
attributes.PutStr(attr.HostedInEC2Environment, val.AsString())
} else {
attributes.PutStr(attr.HostedInEC2Environment, h.name)
}

return nil
}

func (h *ec2HostedInAttributeResolver) Stop(ctx context.Context) error {
return nil
}
125 changes: 125 additions & 0 deletions plugins/processors/awsappsignals/internal/resolver/ec2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package resolver

import (
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/pcommon"

attr "github.com/aws/amazon-cloudwatch-agent/plugins/processors/awsappsignals/internal/attributes"
)

func TestEC2HostedInAttributeResolverWithNoConfiguredName_NoASG_NoEnv(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("")

attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, AttributePlatformEC2, envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithNoConfiguredName_ASGExists_NoEnv(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("")

asgName := "ASG"
attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()
resourceAttributes.PutStr(attr.EC2AutoScalingGroupName, asgName)

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, asgName, envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithConfiguredName_NoASG_NoEnv(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("test")

attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, "test", envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithConfiguredName_ASGExists_NoEnv(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("test")

asgName := "ASG"
attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()
resourceAttributes.PutStr(attr.EC2AutoScalingGroupName, asgName)

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, asgName, envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithNoConfiguredName_NoASG_EnvExists(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("")

envName := "my-env"
attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()
resourceAttributes.PutStr(attr.AWSHostedInEnvironment, envName)

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, envName, envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithConfiguredName_NoASG_EnvExists(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("test")

envName := "my-env"
attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()
resourceAttributes.PutStr(attr.AWSHostedInEnvironment, envName)

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, envName, envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithNoConfiguredName_ASGExists_EnvExists(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("")

asgName := "ASG"
envName := "my-env"
attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()
resourceAttributes.PutStr(attr.EC2AutoScalingGroupName, asgName)
resourceAttributes.PutStr(attr.AWSHostedInEnvironment, envName)

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, envName, envAttr.AsString())
}

func TestEC2HostedInAttributeResolverWithConfiguredName_ASGExists_EnvExists(t *testing.T) {
resolver := newEC2HostedInAttributeResolver("test")

asgName := "ASG"
envName := "my-env"
attributes := pcommon.NewMap()
resourceAttributes := pcommon.NewMap()
resourceAttributes.PutStr(attr.EC2AutoScalingGroupName, asgName)
resourceAttributes.PutStr(attr.AWSHostedInEnvironment, envName)

resolver.Process(attributes, resourceAttributes)
envAttr, ok := attributes.Get(attr.HostedInEC2Environment)
assert.True(t, ok)
assert.Equal(t, envName, envAttr.AsString())
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (t *translator) Translate(conf *confmap.Conf) (component.Config, error) {
}
}

// TODO: Add check for IsAppSignalsEC2() once translation changes are ready for EC2
} else {
cfg.Resolvers = []appsignalsconfig.Resolver{
appsignalsconfig.NewGenericResolver(hostedIn),
Expand Down

0 comments on commit e009c44

Please sign in to comment.