Skip to content

Commit

Permalink
Retrieve instance tags at the same time to reduce number of entities …
Browse files Browse the repository at this point in the history
…in Explore related experience (#1474)
  • Loading branch information
nathalapooja authored Dec 19, 2024
1 parent 3f78406 commit 43d475d
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 207 deletions.
50 changes: 4 additions & 46 deletions extension/entitystore/ec2Info.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ package entitystore
import (
"context"
"errors"
"strings"
"sync"
"time"

"go.uber.org/zap"

"github.com/aws/amazon-cloudwatch-agent/internal/ec2metadataprovider"
"github.com/aws/amazon-cloudwatch-agent/plugins/processors/ec2tagger"
"github.com/aws/amazon-cloudwatch-agent/translator/config"
)

const (
Expand All @@ -28,13 +25,11 @@ const (
)

type EC2Info struct {
InstanceID string
AccountID string
AutoScalingGroup string
InstanceID string
AccountID string

// region is used while making call to describeTags Ec2 API for AutoScalingGroup
Region string
kubernetesMode string
Region string

metadataProvider ec2metadataprovider.MetadataProvider
logger *zap.Logger
Expand All @@ -50,12 +45,6 @@ func (ei *EC2Info) initEc2Info() {
if err := ei.setInstanceIDAccountID(); err != nil {
return
}
// Instance metadata tags is not usable for EKS nodes
// https://github.com/kubernetes/cloud-provider-aws/issues/762
if ei.kubernetesMode != config.ModeEKS {
limitedRetryer := NewRetryer(true, true, defaultJitterMin, defaultJitterMax, ec2tagger.BackoffSleepArray, maxRetry, ei.done, ei.logger)
limitedRetryer.refreshLoop(ei.retrieveAsgName)
}
ei.logger.Debug("Finished initializing EC2Info")
}

Expand All @@ -71,12 +60,6 @@ func (ei *EC2Info) GetAccountID() string {
return ei.AccountID
}

func (ei *EC2Info) GetAutoScalingGroup() string {
ei.mutex.RLock()
defer ei.mutex.RUnlock()
return ei.AutoScalingGroup
}

func (ei *EC2Info) setInstanceIDAccountID() error {
for {
metadataDoc, err := ei.metadataProvider.Get(context.Background())
Expand Down Expand Up @@ -104,34 +87,9 @@ func (ei *EC2Info) setInstanceIDAccountID() error {
}
}

func (ei *EC2Info) retrieveAsgName() error {
tags, err := ei.metadataProvider.InstanceTags(context.Background())
if err != nil {
ei.logger.Debug("Failed to get AutoScalingGroup from instance tags. This is likely because instance tag is not enabled for IMDS but will not affect agent functionality.")
return err
} else if strings.Contains(tags, ec2tagger.Ec2InstanceTagKeyASG) {
asg, err := ei.metadataProvider.InstanceTagValue(context.Background(), ec2tagger.Ec2InstanceTagKeyASG)
if err != nil {
ei.logger.Error("Failed to get AutoScalingGroup through metadata provider", zap.Error(err))
return err
} else {
ei.logger.Debug("AutoScalingGroup retrieved through IMDS")
ei.mutex.Lock()
ei.AutoScalingGroup = asg
if asgLength := len(ei.AutoScalingGroup); asgLength > autoScalingGroupSizeMax {
ei.logger.Warn("AutoScalingGroup length exceeds characters limit and will be ignored", zap.Int("length", asgLength), zap.Int("character limit", autoScalingGroupSizeMax))
ei.AutoScalingGroup = ""
}
ei.mutex.Unlock()
}
}
return nil
}

func newEC2Info(metadataProvider ec2metadataprovider.MetadataProvider, kubernetesMode string, done chan struct{}, region string, logger *zap.Logger) *EC2Info {
func newEC2Info(metadataProvider ec2metadataprovider.MetadataProvider, done chan struct{}, region string, logger *zap.Logger) *EC2Info {
return &EC2Info{
metadataProvider: metadataProvider,
kubernetesMode: kubernetesMode,
done: done,
Region: region,
logger: logger,
Expand Down
112 changes: 0 additions & 112 deletions extension/entitystore/ec2Info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package entitystore
import (
"bytes"
"log"
"strings"
"testing"
"time"

Expand All @@ -15,7 +14,6 @@ import (
"go.uber.org/zap"

"github.com/aws/amazon-cloudwatch-agent/internal/ec2metadataprovider"
"github.com/aws/amazon-cloudwatch-agent/translator/config"
)

var mockedInstanceIdentityDoc = &ec2metadata.EC2InstanceIdentityDocument{
Expand Down Expand Up @@ -87,83 +85,6 @@ func TestSetInstanceIDAccountID(t *testing.T) {
}
}

func TestRetrieveASGName(t *testing.T) {
type args struct {
metadataProvider ec2metadataprovider.MetadataProvider
}
tests := []struct {
name string
args args
wantErr bool
want EC2Info
}{
{
name: "happy path",
args: args{
metadataProvider: &mockMetadataProvider{InstanceIdentityDocument: mockedInstanceIdentityDoc, Tags: map[string]string{"aws:autoscaling:groupName": tagVal3}},
},
wantErr: false,
want: EC2Info{
AutoScalingGroup: tagVal3,
},
},
{
name: "happy path with multiple tags",
args: args{
metadataProvider: &mockMetadataProvider{
InstanceIdentityDocument: mockedInstanceIdentityDoc,
Tags: map[string]string{
"aws:autoscaling:groupName": tagVal3,
"env": "test-env",
"name": "test-name",
}},
},

wantErr: false,
want: EC2Info{
AutoScalingGroup: tagVal3,
},
},
{
name: "AutoScalingGroup too large",
args: args{
metadataProvider: &mockMetadataProvider{
InstanceIdentityDocument: mockedInstanceIdentityDoc,
Tags: map[string]string{
"aws:autoscaling:groupName": strings.Repeat("a", 256),
"env": "test-env",
"name": "test-name",
}},
},

wantErr: false,
want: EC2Info{
AutoScalingGroup: "",
},
},
{
name: "Success IMDS tags call but no ASG",
args: args{
metadataProvider: &mockMetadataProvider{InstanceIdentityDocument: mockedInstanceIdentityDoc, Tags: map[string]string{"name": tagVal3}},
},
wantErr: false,
want: EC2Info{
AutoScalingGroup: "",
},
},
}
for _, tt := range tests {
logger, _ := zap.NewDevelopment()
t.Run(tt.name, func(t *testing.T) {
ei := &EC2Info{metadataProvider: tt.args.metadataProvider, logger: logger}
if err := ei.retrieveAsgName(); (err != nil) != tt.wantErr {
t.Errorf("retrieveAsgName() error = %v, wantErr %v", err, tt.wantErr)
}
assert.Equal(t, tt.want.AutoScalingGroup, ei.GetAutoScalingGroup())
})
}
}

func TestLogMessageDoesNotIncludeResourceInfo(t *testing.T) {
type args struct {
metadataProvider ec2metadataprovider.MetadataProvider
Expand Down Expand Up @@ -202,7 +123,6 @@ func TestLogMessageDoesNotIncludeResourceInfo(t *testing.T) {
logOutput := buf.String()
log.Println(logOutput)
assert.NotContains(t, logOutput, ei.GetInstanceID())
assert.NotContains(t, logOutput, ei.GetAutoScalingGroup())
})
}
}
Expand Down Expand Up @@ -237,35 +157,3 @@ func TestNotInitIfMetadataProviderIsEmpty(t *testing.T) {
})
}
}

func TestNoASGRetrievalInKubernetesMode(t *testing.T) {
type args struct {
metadataProvider ec2metadataprovider.MetadataProvider
kubernetesMode string
}
tests := []struct {
name string
args args
wantErr bool
want string
}{
{
name: "EKSNoASGFromEC2Info",
args: args{
metadataProvider: &mockMetadataProvider{InstanceIdentityDocument: mockedInstanceIdentityDoc, Tags: map[string]string{"aws:autoscaling:groupName": tagVal3}},
kubernetesMode: config.ModeEKS,
},
wantErr: false,
want: "",
},
}
for _, tt := range tests {
logger, _ := zap.NewDevelopment()
t.Run(tt.name, func(t *testing.T) {
ei := &EC2Info{metadataProvider: tt.args.metadataProvider, kubernetesMode: tt.args.kubernetesMode, logger: logger}
go ei.initEc2Info()
time.Sleep(3 * time.Second)
assert.Equal(t, tt.want, ei.GetAutoScalingGroup())
})
}
}
12 changes: 10 additions & 2 deletions extension/entitystore/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type serviceProviderInterface interface {
addEntryForLogGroup(LogGroupName, ServiceAttribute)
logFileServiceAttribute(LogFileGlob, LogGroupName) ServiceAttribute
getServiceNameAndSource() (string, string)
getAutoScalingGroup() string
}

type EntityStore struct {
Expand Down Expand Up @@ -94,7 +95,7 @@ func (e *EntityStore) Start(ctx context.Context, host component.Host) error {
e.serviceprovider = newServiceProvider(e.mode, e.config.Region, &e.ec2Info, e.metadataprovider, getEC2Provider, ec2CredentialConfig, e.done, e.logger)
switch e.mode {
case config.ModeEC2:
e.ec2Info = *newEC2Info(e.metadataprovider, e.kubernetesMode, e.done, e.config.Region, e.logger)
e.ec2Info = *newEC2Info(e.metadataprovider, e.done, e.config.Region, e.logger)
go e.ec2Info.initEc2Info()
// Instance metadata tags is not usable for EKS nodes
// https://github.com/kubernetes/cloud-provider-aws/issues/762
Expand Down Expand Up @@ -177,6 +178,13 @@ func (e *EntityStore) GetServiceMetricAttributesMap() map[string]*string {
return e.createAttributeMap()
}

func (e *EntityStore) GetAutoScalingGroup() string {
if e.serviceprovider == nil {
return ""
}
return e.serviceprovider.getAutoScalingGroup()
}

// AddServiceAttrEntryForLogFile adds an entry to the entity store for the provided file glob -> (serviceName, environmentName) key-value pair
func (e *EntityStore) AddServiceAttrEntryForLogFile(fileGlob LogFileGlob, serviceName string, environmentName string) {
if e.serviceprovider != nil {
Expand Down Expand Up @@ -225,7 +233,7 @@ func (e *EntityStore) createAttributeMap() map[string]*string {

if e.mode == config.ModeEC2 {
addNonEmptyToMap(attributeMap, InstanceIDKey, e.ec2Info.GetInstanceID())
addNonEmptyToMap(attributeMap, ASGKey, e.ec2Info.GetAutoScalingGroup())
addNonEmptyToMap(attributeMap, ASGKey, e.GetAutoScalingGroup())
}
switch e.mode {
case config.ModeEC2:
Expand Down
Loading

0 comments on commit 43d475d

Please sign in to comment.