Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve instance tags at the same time to reduce number of entities in Explore related experience #1474

Merged
merged 13 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading