From b3539bcc72ba2760b2e9f7546f40c6bfa78cb499 Mon Sep 17 00:00:00 2001 From: Rho <13165182+rhoboat@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:45:50 -0700 Subject: [PATCH] Add utilities for autoscaling. (#72) --- awscommons/v2/autoscaling.go | 221 ++++++++++++++++++++++++++++ awscommons/v2/autoscaling_test.go | 235 ++++++++++++++++++++++++++++++ awscommons/v2/errors.go | 68 +++++++++ go.mod | 9 +- go.sum | 10 ++ logging/logging.go | 5 + 6 files changed, 544 insertions(+), 4 deletions(-) create mode 100644 awscommons/v2/autoscaling.go create mode 100644 awscommons/v2/autoscaling_test.go create mode 100644 awscommons/v2/errors.go diff --git a/awscommons/v2/autoscaling.go b/awscommons/v2/autoscaling.go new file mode 100644 index 0000000..ff5b71b --- /dev/null +++ b/awscommons/v2/autoscaling.go @@ -0,0 +1,221 @@ +package awscommons + +import ( + "fmt" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/autoscaling" + autoscalingTypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" + "github.com/gruntwork-io/go-commons/collections" + "github.com/gruntwork-io/go-commons/errors" + "github.com/gruntwork-io/go-commons/logging" + "github.com/gruntwork-io/go-commons/retry" +) + +// GetAsgByName finds the Auto Scaling Group matching the given name. Returns an error if it cannot find a match. +func GetAsgByName(opts *Options, asgName string) (*autoscalingTypes.AutoScalingGroup, error) { + client, err := NewAutoScalingClient(opts) + if err != nil { + return nil, errors.WithStackTrace(err) + } + + input := &autoscaling.DescribeAutoScalingGroupsInput{AutoScalingGroupNames: []string{asgName}} + output, err := client.DescribeAutoScalingGroups(opts.Context, input) + if err != nil { + return nil, errors.WithStackTrace(err) + } + groups := output.AutoScalingGroups + if len(groups) == 0 { + return nil, errors.WithStackTrace(NewLookupError("ASG", asgName, "detailed data")) + } + return &groups[0], nil +} + +// ScaleUp sets the desired capacity, waits until all the instances are available, and returns the new instance IDs. +func ScaleUp( + opts *Options, + asgName string, + originalInstanceIds []string, + desiredCapacity int32, + maxRetries int, + sleepBetweenRetries time.Duration, +) ([]string, error) { + logger := logging.GetProjectLogger() + + client, err := NewAutoScalingClient(opts) + if err != nil { + return nil, err + } + + err = setAsgCapacity(client, opts, asgName, desiredCapacity) + if err != nil { + logger.Errorf("Failed to set ASG desired capacity to %d", desiredCapacity) + logger.Errorf("If the capacity is set in AWS, undo by lowering back to the original desired capacity. If the desired capacity is not yet set, triage the error message below and try again.") + return nil, err + } + + err = waitForCapacity(opts, asgName, maxRetries, sleepBetweenRetries) + if err != nil { + logger.Errorf("Timed out waiting for ASG to reach desired capacity.") + logger.Errorf("Undo by terminating all the new instances and trying again.") + return nil, err + } + + newInstanceIds, err := getLaunchedInstanceIds(opts, asgName, originalInstanceIds) + if err != nil { + logger.Errorf("Error retrieving information about the ASG.") + logger.Errorf("Undo by terminating all the new instances and trying again.") + return nil, err + } + + return newInstanceIds, nil +} + +// getLaunchedInstanceIds returns a list of the newly launched instance IDs in the ASG, given a list of the old instance +// IDs before any change was made. +func getLaunchedInstanceIds(opts *Options, asgName string, existingInstanceIds []string) ([]string, error) { + asg, err := GetAsgByName(opts, asgName) + if err != nil { + return nil, err + } + allInstances := asg.Instances + allInstanceIds := idsFromAsgInstances(allInstances) + newInstanceIds := []string{} + for _, instanceId := range allInstanceIds { + if !collections.ListContainsElement(existingInstanceIds, instanceId) { + newInstanceIds = append(newInstanceIds, instanceId) + } + } + return newInstanceIds, nil +} + +// setAsgCapacity sets the desired capacity on the auto scaling group. This will not wait for the ASG to expand or +// shrink to that size. See waitForCapacity. +func setAsgCapacity(client *autoscaling.Client, opts *Options, asgName string, desiredCapacity int32) error { + logger := logging.GetProjectLogger() + logger.Infof("Updating ASG %s desired capacity to %d.", asgName, desiredCapacity) + + input := &autoscaling.SetDesiredCapacityInput{ + AutoScalingGroupName: aws.String(asgName), + DesiredCapacity: aws.Int32(desiredCapacity), + } + _, err := client.SetDesiredCapacity(opts.Context, input) + if err != nil { + return errors.WithStackTrace(err) + } + + logger.Infof("Requested ASG %s desired capacity to be %d.", asgName, desiredCapacity) + return nil +} + +// SetAsgMaxSize sets the max size on the auto scaling group. Note that updating the max size does not typically +// change the cluster size. +func SetAsgMaxSize(client *autoscaling.Client, opts *Options, asgName string, maxSize int32) error { + logger := logging.GetProjectLogger() + logger.Infof("Updating ASG %s max size to %d.", asgName, maxSize) + + input := &autoscaling.UpdateAutoScalingGroupInput{ + AutoScalingGroupName: aws.String(asgName), + MaxSize: aws.Int32(maxSize), + } + _, err := client.UpdateAutoScalingGroup(opts.Context, input) + if err != nil { + return errors.WithStackTrace(err) + } + + logger.Infof("Requested ASG %s max size to be %d.", asgName, maxSize) + return nil +} + +// waitForCapacity waits for the desired capacity to be reached. +func waitForCapacity( + opts *Options, + asgName string, + maxRetries int, + sleepBetweenRetries time.Duration, +) error { + logger := logging.GetProjectLogger() + logger.Infof("Waiting for ASG %s to reach desired capacity.", asgName) + + err := retry.DoWithRetry( + logger.Logger, + "Waiting for desired capacity to be reached.", + maxRetries, sleepBetweenRetries, + func() error { + logger.Infof("Checking ASG %s capacity.", asgName) + asg, err := GetAsgByName(opts, asgName) + if err != nil { + // TODO: Should we retry this lookup or fail right away? + return retry.FatalError{Underlying: err} + } + + currentCapacity := int32(len(asg.Instances)) + desiredCapacity := *asg.DesiredCapacity + + if currentCapacity == desiredCapacity { + logger.Infof("ASG %s met desired capacity!", asgName) + return nil + } + + logger.Infof("ASG %s not yet at desired capacity %d (current %d).", asgName, desiredCapacity, currentCapacity) + logger.Infof("Waiting for %s...", sleepBetweenRetries) + return errors.WithStackTrace(fmt.Errorf("still waiting for desired capacity to be reached")) + }, + ) + + if err != nil { + return NewCouldNotMeetASGCapacityError( + asgName, + "Error waiting for ASG desired capacity to be reached.", + ) + } + + return nil +} + +// DetachInstances requests AWS to detach the instances, removing them from the ASG. It will also +// request to auto decrement the desired capacity. +func DetachInstances(opts *Options, asgName string, idList []string) error { + logger := logging.GetProjectLogger() + logger.Infof("Detaching %d instances from ASG %s", len(idList), asgName) + + client, err := NewAutoScalingClient(opts) + if err != nil { + return errors.WithStackTrace(err) + } + + // AWS has a 20 instance limit for this, so we detach in groups of 20 ids + for _, smallIDList := range collections.BatchListIntoGroupsOf(idList, 20) { + input := &autoscaling.DetachInstancesInput{ + AutoScalingGroupName: aws.String(asgName), + InstanceIds: smallIDList, + ShouldDecrementDesiredCapacity: aws.Bool(true), + } + _, err := client.DetachInstances(opts.Context, input) + if err != nil { + return errors.WithStackTrace(err) + } + } + + logger.Infof("Detached %d instances from ASG %s", len(idList), asgName) + return nil +} + +// idsFromAsgInstances returns a list of the instance IDs given a list of instance representations from the ASG API. +func idsFromAsgInstances(instances []autoscalingTypes.Instance) []string { + idList := []string{} + for _, inst := range instances { + idList = append(idList, aws.ToString(inst.InstanceId)) + } + return idList +} + +// NewAutoscalingClient returns a new AWS SDK client for interacting with AWS Autoscaling. +func NewAutoScalingClient(opts *Options) (*autoscaling.Client, error) { + cfg, err := NewDefaultConfig(opts) + if err != nil { + return nil, errors.WithStackTrace(err) + } + return autoscaling.NewFromConfig(cfg), nil +} diff --git a/awscommons/v2/autoscaling_test.go b/awscommons/v2/autoscaling_test.go new file mode 100644 index 0000000..3588cd3 --- /dev/null +++ b/awscommons/v2/autoscaling_test.go @@ -0,0 +1,235 @@ +package awscommons + +import ( + "fmt" + "testing" + "time" + + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/autoscaling" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/gruntwork-io/terratest/modules/aws" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/stretchr/testify/require" +) + +func TestGetAsgByNameReturnsCorrectAsg(t *testing.T) { + t.Parallel() + + uniqueID := random.UniqueId() + name := fmt.Sprintf("%s-%s", t.Name(), uniqueID) + otherUniqueID := random.UniqueId() + otherName := fmt.Sprintf("%s-%s", t.Name(), otherUniqueID) + + region := getRandomRegion(t) + opts := NewOptions(region) + + defer terminateEc2InstancesByName(t, region, []string{name, otherName}) + defer deleteAutoScalingGroup(t, name, region) + defer deleteAutoScalingGroup(t, otherName, region) + createTestAutoScalingGroup(t, name, region, 1) + createTestAutoScalingGroup(t, otherName, region, 1) + + asg, err := GetAsgByName(opts, name) + require.NoError(t, err) + require.Equal(t, *asg.AutoScalingGroupName, name) +} + +func TestSetAsgCapacityDeploysNewInstances(t *testing.T) { + t.Parallel() + + uniqueID := random.UniqueId() + name := fmt.Sprintf("%s-%s", t.Name(), uniqueID) + + region := getRandomRegion(t) + opts := NewOptions(region) + client, err := NewAutoScalingClient(opts) + require.NoError(t, err) + + defer terminateEc2InstancesByName(t, region, []string{name}) + defer deleteAutoScalingGroup(t, name, region) + createTestAutoScalingGroup(t, name, region, 1) + + asg, err := GetAsgByName(opts, name) + require.NoError(t, err) + existingInstances := asg.Instances + + require.NoError(t, setAsgCapacity(client, opts, name, 2)) + require.NoError(t, waitForCapacity(opts, name, 40, 15*time.Second)) + + asg, err = GetAsgByName(opts, name) + allInstances := asg.Instances + require.Equal(t, len(allInstances), len(existingInstances)+1) + + existingInstanceIds := idsFromAsgInstances(existingInstances) + newInstanceIds, err := getLaunchedInstanceIds(opts, name, existingInstanceIds) + require.NoError(t, err) + require.Equal(t, len(existingInstanceIds), 1) + require.Equal(t, len(newInstanceIds), 1) + require.NotEqual(t, existingInstanceIds[0], newInstanceIds[0]) +} + +func TestSetAsgCapacityRemovesInstances(t *testing.T) { + t.Parallel() + + uniqueID := random.UniqueId() + name := fmt.Sprintf("%s-%s", t.Name(), uniqueID) + + region := getRandomRegion(t) + opts := NewOptions(region) + client, err := NewAutoScalingClient(opts) + require.NoError(t, err) + + defer terminateEc2InstancesByName(t, region, []string{name}) + defer deleteAutoScalingGroup(t, name, region) + createTestAutoScalingGroup(t, name, region, 2) + + asg, err := GetAsgByName(opts, name) + require.NoError(t, err) + existingInstances := asg.Instances + + require.NoError(t, setAsgCapacity(client, opts, name, 1)) + require.NoError(t, waitForCapacity(opts, name, 40, 15*time.Second)) + + asg, err = GetAsgByName(opts, name) + require.NoError(t, err) + allInstances := asg.Instances + require.Equal(t, len(allInstances), len(existingInstances)-1) +} + +// The following functions were adapted from the tests for cloud-nuke + +func getRandomRegion(t *testing.T) string { + // Use the same regions as those that EKS is available + approvedRegions := []string{"us-west-2", "us-east-1", "us-east-2", "eu-west-1"} + return aws.GetRandomRegion(t, approvedRegions, []string{}) +} + +func createTestAutoScalingGroup(t *testing.T, name string, region string, desiredCount int32) { + instance := createTestEC2Instance(t, region, name) + + opts := NewOptions(region) + client, err := NewAutoScalingClient(opts) + require.NoError(t, err) + + input := &autoscaling.CreateAutoScalingGroupInput{ + AutoScalingGroupName: &name, + InstanceId: instance.InstanceId, + DesiredCapacity: awsgo.Int32(desiredCount), + MinSize: awsgo.Int32(1), + MaxSize: awsgo.Int32(3), + } + _, err = client.CreateAutoScalingGroup(opts.Context, input) + require.NoError(t, err) + + waiter := autoscaling.NewGroupExistsWaiter(client) + err = waiter.Wait(opts.Context, &autoscaling.DescribeAutoScalingGroupsInput{ + AutoScalingGroupNames: []string{name}, + }, 10*time.Minute) + require.NoError(t, err) + + aws.WaitForCapacity(t, name, region, 40, 15*time.Second) +} + +func createTestEC2Instance(t *testing.T, region string, name string) ec2Types.Instance { + opts := NewOptions(region) + ec2Client, err := NewEC2Client(opts) + require.NoError(t, err) + + imageID := aws.GetAmazonLinuxAmi(t, region) + input := &ec2.RunInstancesInput{ + ImageId: awsgo.String(imageID), + InstanceType: ec2Types.InstanceTypeT2Micro, + MinCount: awsgo.Int32(1), + MaxCount: awsgo.Int32(1), + } + runResult, err := ec2Client.RunInstances(opts.Context, input) + require.NoError(t, err) + + require.NotEqual(t, len(runResult.Instances), 0) + + waiter := ec2.NewInstanceExistsWaiter(ec2Client) + err = waiter.Wait(opts.Context, &ec2.DescribeInstancesInput{ + Filters: []ec2Types.Filter{ + { + Name: awsgo.String("instance-id"), + Values: []string{*runResult.Instances[0].InstanceId}, + }, + }, + }, 10*time.Minute) + require.NoError(t, err) + + // Add test tag to the created instance + _, err = ec2Client.CreateTags(opts.Context, &ec2.CreateTagsInput{ + Resources: []string{*runResult.Instances[0].InstanceId}, + Tags: []ec2Types.Tag{ + { + Key: awsgo.String("Name"), + Value: awsgo.String(name), + }, + }, + }) + require.NoError(t, err) + + // EC2 Instance must be in a running before this function returns + runningWaiter := ec2.NewInstanceRunningWaiter(ec2Client) + err = runningWaiter.Wait(opts.Context, &ec2.DescribeInstancesInput{ + Filters: []ec2Types.Filter{ + { + Name: awsgo.String("instance-id"), + Values: []string{*runResult.Instances[0].InstanceId}, + }, + }, + }, 10*time.Minute) + require.NoError(t, err) + + return runResult.Instances[0] +} + +func terminateEc2InstancesByName(t *testing.T, region string, names []string) { + for _, name := range names { + instanceIds := aws.GetEc2InstanceIdsByTag(t, region, "Name", name) + for _, instanceID := range instanceIds { + aws.TerminateInstance(t, region, instanceID) + } + } +} + +func deleteAutoScalingGroup(t *testing.T, name string, region string) { + // We have to scale ASG down to 0 before we can delete it + scaleAsgToZero(t, name, region) + + opts := NewOptions(region) + client, err := NewAutoScalingClient(opts) + require.NoError(t, err) + + input := &autoscaling.DeleteAutoScalingGroupInput{AutoScalingGroupName: awsgo.String(name)} + _, err = client.DeleteAutoScalingGroup(opts.Context, input) + require.NoError(t, err) + waiter := autoscaling.NewGroupNotExistsWaiter(client) + err = waiter.Wait(opts.Context, &autoscaling.DescribeAutoScalingGroupsInput{ + AutoScalingGroupNames: []string{name}, + }, 10*time.Minute) + require.NoError(t, err) +} + +func scaleAsgToZero(t *testing.T, name string, region string) { + opts := NewOptions(region) + client, err := NewAutoScalingClient(opts) + require.NoError(t, err) + + input := &autoscaling.UpdateAutoScalingGroupInput{ + AutoScalingGroupName: awsgo.String(name), + DesiredCapacity: awsgo.Int32(0), + MinSize: awsgo.Int32(0), + MaxSize: awsgo.Int32(0), + } + _, err = client.UpdateAutoScalingGroup(opts.Context, input) + require.NoError(t, err) + aws.WaitForCapacity(t, name, region, 40, 15*time.Second) + + // There is an eventual consistency bug where even though the ASG is scaled down, AWS sometimes still views a + // scaling activity so we add a 5 second pause here to work around it. + time.Sleep(5 * time.Second) +} diff --git a/awscommons/v2/errors.go b/awscommons/v2/errors.go new file mode 100644 index 0000000..449c7ca --- /dev/null +++ b/awscommons/v2/errors.go @@ -0,0 +1,68 @@ +package awscommons + +import ( + "fmt" + "strings" +) + +// MultipleLookupErrors represents multiple errors found while looking up a resource +type MultipleLookupErrors struct { + errors []error +} + +func (err MultipleLookupErrors) Error() string { + messages := []string{ + fmt.Sprintf("%d errors found during lookup:", len(err.errors)), + } + + for _, individualErr := range err.errors { + messages = append(messages, individualErr.Error()) + } + return strings.Join(messages, "\n") +} + +func (err MultipleLookupErrors) AddError(newErr error) { + err.errors = append(err.errors, newErr) +} + +func (err MultipleLookupErrors) IsEmpty() bool { + return len(err.errors) == 0 +} + +func NewMultipleLookupErrors() MultipleLookupErrors { + return MultipleLookupErrors{[]error{}} +} + +// LookupError represents an error related to looking up data on an object. +type LookupError struct { + objectProperty string + objectType string + objectId string +} + +func (err LookupError) Error() string { + return fmt.Sprintf("Failed to look up %s for %s with id %s.", err.objectProperty, err.objectType, err.objectId) +} + +// NewLookupError constructs a new LookupError object that can be used to return an error related to a look up error. +func NewLookupError(objectType string, objectId string, objectProperty string) LookupError { + return LookupError{objectProperty: objectProperty, objectType: objectType, objectId: objectId} +} + +// CouldNotMeetASGCapacityError represents an error related to waiting for ASG to reach desired capacity. +type CouldNotMeetASGCapacityError struct { + asgName string + message string +} + +func (err CouldNotMeetASGCapacityError) Error() string { + return fmt.Sprintf( + "Could not reach desired capacity of ASG %s: %s", + err.asgName, + err.message, + ) +} + +func NewCouldNotMeetASGCapacityError(asgName string, message string) CouldNotMeetASGCapacityError { + return CouldNotMeetASGCapacityError{asgName, message} +} diff --git a/go.mod b/go.mod index 68f8237..d10a2a1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/aws/aws-sdk-go v1.44.48 - github.com/aws/aws-sdk-go-v2 v1.16.7 + github.com/aws/aws-sdk-go-v2 v1.16.16 github.com/aws/aws-sdk-go-v2/config v1.15.13 github.com/aws/aws-sdk-go-v2/service/ec2 v1.47.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.27.1 @@ -32,17 +32,18 @@ require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.3 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.12.8 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.8 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.15 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.5 // indirect + github.com/aws/aws-sdk-go-v2/service/autoscaling v1.23.16 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.9 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.11.11 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.16.9 // indirect - github.com/aws/smithy-go v1.12.0 // indirect + github.com/aws/smithy-go v1.13.3 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect diff --git a/go.sum b/go.sum index fd36c12..62b6078 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,8 @@ github.com/aws/aws-sdk-go v1.44.48 h1:jLDC9RsNoYMLFlKpB8LdqUnoDdC2yvkS4QbuyPQJ8+ github.com/aws/aws-sdk-go v1.44.48/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.16.7 h1:zfBwXus3u14OszRxGcqCDS4MfMCv10e8SMJ2r8Xm0Ns= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= +github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk= +github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.3 h1:S/ZBwevQkr7gv5YxONYpGQxlMFFYSRfz3RMcjsC9Qhk= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.3/go.mod h1:gNsR5CaXKmQSSzrmGxmwmct/r+ZBfbxorAuXYsj/M5Y= github.com/aws/aws-sdk-go-v2/config v1.15.13 h1:CJH9zn/Enst7lDiGpoguVt0lZr5HcpNVlRJWbJ6qreo= @@ -76,12 +78,18 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.8 h1:VfBdn2AxwMbFyJN/lF/xuT3 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.8/go.mod h1:oL1Q3KuCq1D4NykQnIvtRiBGLUXhcpY5pl6QZB2XEPU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14 h1:2C0pYHcUBmdzPj+EKNC4qj97oK6yjrUhc1KoSodglvk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 h1:s4g/wnzMf+qepSNgTvaQQHNxyMLKSawNhKCPNy++2xY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8 h1:2J+jdlBJWEmTyAwC82Ym68xCykIvnSnIN18b8xHGlcc= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 h1:/K482T5A3623WJgWT8w1yRAFK4RzGzEl7y39yhtn9eA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.15 h1:QquxR7NH3ULBsKC+NoTpilzbKKS+5AELfNREInbhvas= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.15/go.mod h1:Tkrthp/0sNBShQQsamR7j/zY4p19tVTAs+nnqhH6R3c= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.5 h1:tEEHn+PGAxRVqMPEhtU8oCSW/1Ge3zP5nUgPrGQNUPs= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.5/go.mod h1:aIwFF3dUk95ocCcA3zfk3nhz0oLkpzHFWuMp8l/4nNs= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.23.16 h1:cp30gVVAbZfeDod6UJGppMH2+p+/cRCG2AZ1TbT+LqA= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.23.16/go.mod h1:hHTMeJt6CQwFdmS19RK1LsDscus8c25Ve8KiYRhsISg= github.com/aws/aws-sdk-go-v2/service/ec2 v1.47.2 h1:81hrDgbXHL44WdY6M/fHGXLlv17qTpOFzutXRVDEk3Y= github.com/aws/aws-sdk-go-v2/service/ec2 v1.47.2/go.mod h1:VoBcwURHnJVCWuXHdqVuG03i2lUlHJ5DTTqDSyCdEcc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.3 h1:4n4KCtv5SUoT5Er5XV41huuzrCqepxlW3SDI9qHQebc= @@ -102,6 +110,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.16.9 h1:yOfILxyjmtr2ubRkRJldlHDFBhf5 github.com/aws/aws-sdk-go-v2/service/sts v1.16.9/go.mod h1:O1IvkYxr+39hRf960Us6j0x1P8pDqhTX+oXM5kQNl/Y= github.com/aws/smithy-go v1.12.0 h1:gXpeZel/jPoWQ7OEmLIgCUnhkFftqNfwWUwAHSlp1v0= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.13.3 h1:l7LYxGuzK6/K+NzJ2mC+VvLUbae0sL3bXU//04MkmnA= +github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= diff --git a/logging/logging.go b/logging/logging.go index 8c1d807..358de0c 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -9,6 +9,11 @@ import ( var globalLogLevel = logrus.InfoLevel var globalLogLevelLock = sync.Mutex{} +func GetProjectLogger() *logrus.Entry { + logger := GetLogger("") + return logger.WithField("name", "go-commons") +} + // Create a new logger with the given name func GetLogger(name string) *logrus.Logger { logger := logrus.New()