diff --git a/discovery/aws/ec2.go b/discovery/aws/ec2.go index 7e35a1807f..431bbcd811 100644 --- a/discovery/aws/ec2.go +++ b/discovery/aws/ec2.go @@ -23,14 +23,14 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" - "github.com/aws/aws-sdk-go/aws/ec2metadata" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + awsConfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/aws/smithy-go" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -124,17 +124,22 @@ func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } if c.Region == "" { - sess, err := session.NewSession() + cfg, err := awsConfig.LoadDefaultConfig(context.Background()) if err != nil { return err } - metadata := ec2metadata.New(sess) - region, err := metadata.Region() - if err != nil { - return errors.New("EC2 SD configuration requires a region") + + if cfg.Region != "" { + // If the region is already set in the config, use it. + // This can happen if the user has set the region in the AWS config file or environment variables. + c.Region = cfg.Region } - c.Region = region } + + if c.Region == "" { + return errors.New("EC2 SD configuration requires a region") + } + for _, f := range c.Filters { if len(f.Values) == 0 { return errors.New("EC2 SD configuration filter values cannot be empty") @@ -143,13 +148,18 @@ func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return c.HTTPClientConfig.Validate() } +type ec2Client interface { + DescribeAvailabilityZones(ctx context.Context, params *ec2.DescribeAvailabilityZonesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error) + DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) +} + // EC2Discovery periodically performs EC2-SD requests. It implements // the Discoverer interface. type EC2Discovery struct { *refresh.Discovery logger *slog.Logger cfg *EC2SDConfig - ec2 ec2iface.EC2API + ec2 ec2Client // azToAZID maps this account's availability zones to their underlying AZ // ID, e.g. eu-west-2a -> euw2-az2. Refreshes are performed sequentially, so @@ -183,46 +193,43 @@ func NewEC2Discovery(conf *EC2SDConfig, logger *slog.Logger, metrics discovery.D return d, nil } -func (d *EC2Discovery) ec2Client(context.Context) (ec2iface.EC2API, error) { +func (d *EC2Discovery) ec2Client(ctx context.Context) (ec2Client, error) { if d.ec2 != nil { return d.ec2, nil } + credProvider := credentials.NewStaticCredentialsProvider(d.cfg.AccessKey, string(d.cfg.SecretKey), "") - creds := credentials.NewStaticCredentials(d.cfg.AccessKey, string(d.cfg.SecretKey), "") - if d.cfg.AccessKey == "" && d.cfg.SecretKey == "" { - creds = nil - } - - client, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "ec2_sd") + // Build the HTTP client from the provided HTTPClientConfig. + httpClient, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "ec2_sd") if err != nil { return nil, err } - sess, err := session.NewSessionWithOptions(session.Options{ - Config: aws.Config{ - Endpoint: &d.cfg.Endpoint, - Region: &d.cfg.Region, - Credentials: creds, - HTTPClient: client, - }, - Profile: d.cfg.Profile, - }) + // Build the AWS config with the provided region and credentials. + cfg, err := awsConfig.LoadDefaultConfig( + ctx, + awsConfig.WithRegion(d.cfg.Region), + awsConfig.WithCredentialsProvider(credProvider), + awsConfig.WithSharedConfigProfile(d.cfg.Profile), + awsConfig.WithHTTPClient(httpClient), + ) if err != nil { - return nil, fmt.Errorf("could not create aws session: %w", err) + return nil, fmt.Errorf("could not create aws config: %w", err) } + // If the role ARN is set, assume the role to get credentials and set the credentials provider in the config. if d.cfg.RoleARN != "" { - creds := stscreds.NewCredentials(sess, d.cfg.RoleARN) - d.ec2 = ec2.New(sess, &aws.Config{Credentials: creds}) - } else { - d.ec2 = ec2.New(sess) + assumeProvider := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), d.cfg.RoleARN) + cfg.Credentials = aws.NewCredentialsCache(assumeProvider) } + d.ec2 = ec2.NewFromConfig(cfg) + return d.ec2, nil } func (d *EC2Discovery) refreshAZIDs(ctx context.Context) error { - azs, err := d.ec2.DescribeAvailabilityZonesWithContext(ctx, &ec2.DescribeAvailabilityZonesInput{}) + azs, err := d.ec2.DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{}) if err != nil { return err } @@ -243,11 +250,11 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error Source: d.cfg.Region, } - var filters []*ec2.Filter + var filters []ec2Types.Filter for _, f := range d.cfg.Filters { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2Types.Filter{ Name: aws.String(f.Name), - Values: aws.StringSlice(f.Values), + Values: f.Values, }) } @@ -262,7 +269,18 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error } input := &ec2.DescribeInstancesInput{Filters: filters} - if err := ec2Client.DescribeInstancesPagesWithContext(ctx, input, func(p *ec2.DescribeInstancesOutput, _ bool) bool { + paginator := ec2.NewDescribeInstancesPaginator(ec2Client, input) + + for paginator.HasMorePages() { + p, err := paginator.NextPage(ctx) + if err != nil { + var awsErr smithy.APIError + if errors.As(err, &awsErr) && (awsErr.ErrorCode() == "AuthFailure" || awsErr.ErrorCode() == "UnauthorizedOperation") { + d.ec2 = nil + } + return nil, fmt.Errorf("could not describe instances: %w", err) + } + for _, r := range p.Reservations { for _, inst := range r.Instances { if inst.PrivateIpAddress == nil { @@ -285,8 +303,8 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error addr := net.JoinHostPort(*inst.PrivateIpAddress, strconv.Itoa(d.cfg.Port)) labels[model.AddressLabel] = model.LabelValue(addr) - if inst.Platform != nil { - labels[ec2LabelPlatform] = model.LabelValue(*inst.Platform) + if inst.Platform != "" { + labels[ec2LabelPlatform] = model.LabelValue(inst.Platform) } if inst.PublicIpAddress != nil { @@ -302,15 +320,15 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error "az", *inst.Placement.AvailabilityZone) } labels[ec2LabelAZID] = model.LabelValue(azID) - labels[ec2LabelInstanceState] = model.LabelValue(*inst.State.Name) - labels[ec2LabelInstanceType] = model.LabelValue(*inst.InstanceType) + labels[ec2LabelInstanceState] = model.LabelValue(inst.State.Name) + labels[ec2LabelInstanceType] = model.LabelValue(inst.InstanceType) - if inst.InstanceLifecycle != nil { - labels[ec2LabelInstanceLifecycle] = model.LabelValue(*inst.InstanceLifecycle) + if inst.InstanceLifecycle != "" { + labels[ec2LabelInstanceLifecycle] = model.LabelValue(inst.InstanceLifecycle) } - if inst.Architecture != nil { - labels[ec2LabelArch] = model.LabelValue(*inst.Architecture) + if inst.Architecture != "" { + labels[ec2LabelArch] = model.LabelValue(inst.Architecture) } if inst.VpcId != nil { @@ -337,7 +355,7 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error // we might have to extend the slice with more than one element // that could leave empty strings in the list which is intentional // to keep the position/device index information - for int64(len(primaryipv6addrs)) <= *eni.Attachment.DeviceIndex { + for int32(len(primaryipv6addrs)) <= *eni.Attachment.DeviceIndex { primaryipv6addrs = append(primaryipv6addrs, "") } primaryipv6addrs[*eni.Attachment.DeviceIndex] = *ipv6addr.Ipv6Address @@ -363,7 +381,7 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error } for _, t := range inst.Tags { - if t == nil || t.Key == nil || t.Value == nil { + if t.Key == nil || t.Value == nil { continue } name := strutil.SanitizeLabelName(*t.Key) @@ -372,13 +390,7 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error tg.Targets = append(tg.Targets, labels) } } - return true - }); err != nil { - var awsErr awserr.Error - if errors.As(err, &awsErr) && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") { - d.ec2 = nil - } - return nil, fmt.Errorf("could not describe instances: %w", err) } + return []*targetgroup.Group{tg}, nil } diff --git a/discovery/aws/ec2_test.go b/discovery/aws/ec2_test.go index 2955e0e02e..8a8fd6e244 100644 --- a/discovery/aws/ec2_test.go +++ b/discovery/aws/ec2_test.go @@ -18,10 +18,9 @@ import ( "errors" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" "go.uber.org/goleak" @@ -39,10 +38,6 @@ func boolptr(b bool) *bool { return &b } -func int64ptr(i int64) *int64 { - return &i -} - // Struct for test data. type ec2DataStore struct { region string @@ -51,7 +46,7 @@ type ec2DataStore struct { ownerID string - instances []*ec2.Instance + instances []ec2Types.Instance } // The tests itself. @@ -121,7 +116,7 @@ func TestEC2DiscoveryRefresh(t *testing.T) { "azname-b": "azid-2", "azname-c": "azid-3", }, - instances: []*ec2.Instance{ + instances: []ec2Types.Instance{ { InstanceId: strptr("instance-id-noprivateip"), }, @@ -143,26 +138,26 @@ func TestEC2DiscoveryRefresh(t *testing.T) { "azname-c": "azid-3", }, ownerID: "owner-id-novpc", - instances: []*ec2.Instance{ + instances: []ec2Types.Instance{ { // set every possible options and test them here - Architecture: strptr("architecture-novpc"), + Architecture: "architecture-novpc", ImageId: strptr("ami-novpc"), InstanceId: strptr("instance-id-novpc"), - InstanceLifecycle: strptr("instance-lifecycle-novpc"), - InstanceType: strptr("instance-type-novpc"), - Placement: &ec2.Placement{AvailabilityZone: strptr("azname-b")}, - Platform: strptr("platform-novpc"), + InstanceLifecycle: "instance-lifecycle-novpc", + InstanceType: "instance-type-novpc", + Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-b")}, + Platform: "platform-novpc", PrivateDnsName: strptr("private-dns-novpc"), PrivateIpAddress: strptr("1.2.3.4"), PublicDnsName: strptr("public-dns-novpc"), PublicIpAddress: strptr("42.42.42.2"), - State: &ec2.InstanceState{Name: strptr("running")}, + State: &ec2Types.InstanceState{Name: "running"}, // test tags once and for all - Tags: []*ec2.Tag{ + Tags: []ec2Types.Tag{ {Key: strptr("tag-1-key"), Value: strptr("tag-1-value")}, {Key: strptr("tag-2-key"), Value: strptr("tag-2-value")}, - nil, + {}, {Value: strptr("tag-4-value")}, {Key: strptr("tag-5-key")}, }, @@ -206,22 +201,22 @@ func TestEC2DiscoveryRefresh(t *testing.T) { "azname-b": "azid-2", "azname-c": "azid-3", }, - instances: []*ec2.Instance{ + instances: []ec2Types.Instance{ { // just the minimum needed for the refresh work ImageId: strptr("ami-ipv4"), InstanceId: strptr("instance-id-ipv4"), - InstanceType: strptr("instance-type-ipv4"), - Placement: &ec2.Placement{AvailabilityZone: strptr("azname-c")}, + InstanceType: "instance-type-ipv4", + Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-c")}, PrivateIpAddress: strptr("5.6.7.8"), - State: &ec2.InstanceState{Name: strptr("running")}, + State: &ec2Types.InstanceState{Name: "running"}, SubnetId: strptr("azid-3"), VpcId: strptr("vpc-ipv4"), // network interfaces - NetworkInterfaces: []*ec2.InstanceNetworkInterface{ + NetworkInterfaces: []ec2Types.InstanceNetworkInterface{ // interface without subnet -> should be ignored { - Ipv6Addresses: []*ec2.InstanceIpv6Address{ + Ipv6Addresses: []ec2Types.InstanceIpv6Address{ { Ipv6Address: strptr("2001:db8:1::1"), IsPrimaryIpv6: boolptr(true), @@ -230,12 +225,12 @@ func TestEC2DiscoveryRefresh(t *testing.T) { }, // interface with subnet, no IPv6 { - Ipv6Addresses: []*ec2.InstanceIpv6Address{}, + Ipv6Addresses: []ec2Types.InstanceIpv6Address{}, SubnetId: strptr("azid-3"), }, // interface with another subnet, no IPv6 { - Ipv6Addresses: []*ec2.InstanceIpv6Address{}, + Ipv6Addresses: []ec2Types.InstanceIpv6Address{}, SubnetId: strptr("azid-1"), }, }, @@ -274,25 +269,25 @@ func TestEC2DiscoveryRefresh(t *testing.T) { "azname-b": "azid-2", "azname-c": "azid-3", }, - instances: []*ec2.Instance{ + instances: []ec2Types.Instance{ { // just the minimum needed for the refresh work ImageId: strptr("ami-ipv6"), InstanceId: strptr("instance-id-ipv6"), - InstanceType: strptr("instance-type-ipv6"), - Placement: &ec2.Placement{AvailabilityZone: strptr("azname-b")}, + InstanceType: "instance-type-ipv6", + Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-b")}, PrivateIpAddress: strptr("9.10.11.12"), - State: &ec2.InstanceState{Name: strptr("running")}, + State: &ec2Types.InstanceState{Name: "running"}, SubnetId: strptr("azid-2"), VpcId: strptr("vpc-ipv6"), // network interfaces - NetworkInterfaces: []*ec2.InstanceNetworkInterface{ + NetworkInterfaces: []ec2Types.InstanceNetworkInterface{ // interface without primary IPv6, index 2 { - Attachment: &ec2.InstanceNetworkInterfaceAttachment{ - DeviceIndex: int64ptr(3), + Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{ + DeviceIndex: aws.Int32(3), }, - Ipv6Addresses: []*ec2.InstanceIpv6Address{ + Ipv6Addresses: []ec2Types.InstanceIpv6Address{ { Ipv6Address: strptr("2001:db8:2::1:1"), IsPrimaryIpv6: boolptr(false), @@ -302,10 +297,10 @@ func TestEC2DiscoveryRefresh(t *testing.T) { }, // interface with primary IPv6, index 1 { - Attachment: &ec2.InstanceNetworkInterfaceAttachment{ - DeviceIndex: int64ptr(1), + Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{ + DeviceIndex: aws.Int32(1), }, - Ipv6Addresses: []*ec2.InstanceIpv6Address{ + Ipv6Addresses: []ec2Types.InstanceIpv6Address{ { Ipv6Address: strptr("2001:db8:2::2:1"), IsPrimaryIpv6: boolptr(false), @@ -319,10 +314,10 @@ func TestEC2DiscoveryRefresh(t *testing.T) { }, // interface with primary IPv6, index 3 { - Attachment: &ec2.InstanceNetworkInterfaceAttachment{ - DeviceIndex: int64ptr(3), + Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{ + DeviceIndex: aws.Int32(3), }, - Ipv6Addresses: []*ec2.InstanceIpv6Address{ + Ipv6Addresses: []ec2Types.InstanceIpv6Address{ { Ipv6Address: strptr("2001:db8:2::3:1"), IsPrimaryIpv6: boolptr(true), @@ -332,10 +327,10 @@ func TestEC2DiscoveryRefresh(t *testing.T) { }, // interface without primary IPv6, index 0 { - Attachment: &ec2.InstanceNetworkInterfaceAttachment{ - DeviceIndex: int64ptr(0), + Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{ + DeviceIndex: aws.Int32(0), }, - Ipv6Addresses: []*ec2.InstanceIpv6Address{}, + Ipv6Addresses: []ec2Types.InstanceIpv6Address{}, SubnetId: strptr("azid-3"), }, }, @@ -388,7 +383,6 @@ func TestEC2DiscoveryRefresh(t *testing.T) { // EC2 client mock. type mockEC2Client struct { - ec2iface.EC2API ec2Data ec2DataStore } @@ -399,16 +393,16 @@ func newMockEC2Client(ec2Data *ec2DataStore) *mockEC2Client { return &client } -func (m *mockEC2Client) DescribeAvailabilityZonesWithContext(_ aws.Context, _ *ec2.DescribeAvailabilityZonesInput, _ ...request.Option) (*ec2.DescribeAvailabilityZonesOutput, error) { +func (m *mockEC2Client) DescribeAvailabilityZones(_ context.Context, _ *ec2.DescribeAvailabilityZonesInput, _ ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error) { if len(m.ec2Data.azToAZID) == 0 { return nil, errors.New("No AZs found") } - azs := make([]*ec2.AvailabilityZone, len(m.ec2Data.azToAZID)) + azs := make([]ec2Types.AvailabilityZone, len(m.ec2Data.azToAZID)) i := 0 for k, v := range m.ec2Data.azToAZID { - azs[i] = &ec2.AvailabilityZone{ + azs[i] = ec2Types.AvailabilityZone{ ZoneName: strptr(k), ZoneId: strptr(v), } @@ -420,15 +414,13 @@ func (m *mockEC2Client) DescribeAvailabilityZonesWithContext(_ aws.Context, _ *e }, nil } -func (m *mockEC2Client) DescribeInstancesPagesWithContext(_ aws.Context, _ *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, _ ...request.Option) error { - r := ec2.Reservation{} - r.SetInstances(m.ec2Data.instances) - r.SetOwnerId(m.ec2Data.ownerID) +func (m *mockEC2Client) DescribeInstances(_ context.Context, _ *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { + r := ec2Types.Reservation{} + r.Instances = append(r.Instances, m.ec2Data.instances...) + r.OwnerId = aws.String(m.ec2Data.ownerID) o := ec2.DescribeInstancesOutput{} - o.SetReservations([]*ec2.Reservation{&r}) + o.Reservations = []ec2Types.Reservation{r} - _ = fn(&o, true) - - return nil + return &o, nil } diff --git a/discovery/aws/lightsail.go b/discovery/aws/lightsail.go index ff1059ede0..62c64afcfd 100644 --- a/discovery/aws/lightsail.go +++ b/discovery/aws/lightsail.go @@ -23,13 +23,13 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" - "github.com/aws/aws-sdk-go/aws/ec2metadata" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/aws/aws-sdk-go-v2/aws" + awsConfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/lightsail" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/aws/smithy-go" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/config" "github.com/prometheus/common/model" @@ -106,20 +106,19 @@ func (c *LightsailSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) err return err } if c.Region == "" { - sess, err := session.NewSession() + cfg, err := awsConfig.LoadDefaultConfig(context.Background()) if err != nil { return err } - metadata := ec2metadata.New(sess) - - region, err := metadata.Region() - if err != nil { - //nolint:staticcheck // Capitalized first word. - return errors.New("Lightsail SD configuration requires a region") - } - c.Region = region + // Use the region from the AWS config. It will load environment variables and shared config files. + c.Region = cfg.Region } + + if c.Region == "" { + return errors.New("lightsail SD configuration requires a region") + } + return c.HTTPClientConfig.Validate() } @@ -128,7 +127,7 @@ func (c *LightsailSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) err type LightsailDiscovery struct { *refresh.Discovery cfg *LightsailSDConfig - lightsail *lightsail.Lightsail + lightsail *lightsail.Client } // NewLightsailDiscovery returns a new LightsailDiscovery which periodically refreshes its targets. @@ -157,46 +156,44 @@ func NewLightsailDiscovery(conf *LightsailSDConfig, logger *slog.Logger, metrics return d, nil } -func (d *LightsailDiscovery) lightsailClient() (*lightsail.Lightsail, error) { +func (d *LightsailDiscovery) lightsailClient(ctx context.Context) (*lightsail.Client, error) { if d.lightsail != nil { return d.lightsail, nil } - creds := credentials.NewStaticCredentials(d.cfg.AccessKey, string(d.cfg.SecretKey), "") - if d.cfg.AccessKey == "" && d.cfg.SecretKey == "" { - creds = nil - } + credProvider := credentials.NewStaticCredentialsProvider(d.cfg.AccessKey, string(d.cfg.SecretKey), "") - client, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "lightsail_sd") + // Build the HTTP client from the provided HTTPClientConfig. + httpClient, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "lightsail_sd") if err != nil { return nil, err } - sess, err := session.NewSessionWithOptions(session.Options{ - Config: aws.Config{ - Endpoint: &d.cfg.Endpoint, - Region: &d.cfg.Region, - Credentials: creds, - HTTPClient: client, - }, - Profile: d.cfg.Profile, - }) + // Build the AWS config with the provided region and credentials. + cfg, err := awsConfig.LoadDefaultConfig( + ctx, + awsConfig.WithRegion(d.cfg.Region), + awsConfig.WithCredentialsProvider(credProvider), + awsConfig.WithSharedConfigProfile(d.cfg.Profile), + awsConfig.WithHTTPClient(httpClient), + ) if err != nil { - return nil, fmt.Errorf("could not create aws session: %w", err) + return nil, fmt.Errorf("could not create aws config: %w", err) } + // If the role ARN is set, assume the role to get credentials and set the credentials provider in the config. if d.cfg.RoleARN != "" { - creds := stscreds.NewCredentials(sess, d.cfg.RoleARN) - d.lightsail = lightsail.New(sess, &aws.Config{Credentials: creds}) - } else { - d.lightsail = lightsail.New(sess) + assumeProvider := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), d.cfg.RoleARN) + cfg.Credentials = aws.NewCredentialsCache(assumeProvider) } + d.lightsail = lightsail.NewFromConfig(cfg) + return d.lightsail, nil } func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { - lightsailClient, err := d.lightsailClient() + lightsailClient, err := d.lightsailClient(ctx) if err != nil { return nil, err } @@ -207,10 +204,10 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, input := &lightsail.GetInstancesInput{} - output, err := lightsailClient.GetInstancesWithContext(ctx, input) + output, err := lightsailClient.GetInstances(ctx, input) if err != nil { - var awsErr awserr.Error - if errors.As(err, &awsErr) && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") { + var awsErr smithy.APIError + if errors.As(err, &awsErr) && (awsErr.ErrorCode() == "AuthFailure" || awsErr.ErrorCode() == "UnauthorizedOperation") { d.lightsail = nil } return nil, fmt.Errorf("could not get instances: %w", err) @@ -241,9 +238,7 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, if len(inst.Ipv6Addresses) > 0 { var ipv6addrs []string - for _, ipv6addr := range inst.Ipv6Addresses { - ipv6addrs = append(ipv6addrs, *ipv6addr) - } + ipv6addrs = append(ipv6addrs, inst.Ipv6Addresses...) labels[lightsailLabelIPv6Addresses] = model.LabelValue( lightsailLabelSeparator + strings.Join(ipv6addrs, lightsailLabelSeparator) + @@ -251,7 +246,7 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, } for _, t := range inst.Tags { - if t == nil || t.Key == nil || t.Value == nil { + if t.Key == nil || t.Value == nil { continue } name := strutil.SanitizeLabelName(*t.Key) diff --git a/go.mod b/go.mod index e993819936..a1c996db37 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,13 @@ require ( github.com/KimMachineGun/automemlimit v0.7.3 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b - github.com/aws/aws-sdk-go v1.55.7 + github.com/aws/aws-sdk-go-v2 v1.37.0 + github.com/aws/aws-sdk-go-v2/config v1.29.14 + github.com/aws/aws-sdk-go-v2/credentials v1.17.67 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.237.0 + github.com/aws/aws-sdk-go-v2/service/lightsail v1.44.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 + github.com/aws/smithy-go v1.22.5 github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 github.com/cespare/xxhash/v2 v2.3.0 github.com/dennwc/varint v1.0.0 @@ -95,19 +101,14 @@ require ( ) require ( - github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect - github.com/aws/smithy-go v1.22.2 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -174,7 +175,6 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect diff --git a/go.sum b/go.sum index 14a9b22ff1..233ad79468 100644 --- a/go.sum +++ b/go.sum @@ -49,34 +49,36 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= -github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= -github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2 v1.37.0 h1:YtCOESR/pN4j5oA7cVHSfOwIcuh/KwHC4DOSXFbv5F0= +github.com/aws/aws-sdk-go-v2 v1.37.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0 h1:H2iZoqW/v2Jnrh1FnU725Bq6KJ0k2uP63yH+DcY+HUI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0/go.mod h1:L0FqLbwMXHvNC/7crWV1iIxUlOKYZUE8KuTIA+TozAI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0 h1:EDped/rNzAhFPhVY0sDGbtD16OKqksfA8OjF/kLEgw8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0/go.mod h1:uUI335jvzpZRPpjYx6ODc/wg1qH+NnoSTK/FwVeK0C0= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.237.0 h1:XHE2G+yaDQql32FZt19QmQt4WuisqQJIkMUSCxeCUl8= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.237.0/go.mod h1:t11/j/nH9i6bbsPH9xc04BJOsV2nVPUqrB67/TLDsyM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.0 h1:eRhU3Sh8dGbaniI6B+I48XJMrTPRkK4DKo+vqIxziOU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.0/go.mod h1:paNLV18DZ6FnWE/bd06RIKPDIFpjuvCkGKWTG/GDBeM= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.44.0 h1:QiiCqpKy0prxq+92uWfESzcb7/8Y9JAamcMOzVYLEoM= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.44.0/go.mod h1:ESppxYqXQCpCY+KWl3BdkQjmsQX6zxKP39SnDtRDoU0= github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= +github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -289,10 +291,6 @@ github.com/ionos-cloud/sdk-go/v6 v6.3.4 h1:jTvGl4LOF8v8OYoEIBNVwbFoqSGAFqn6vGE7s github.com/ionos-cloud/sdk-go/v6 v6.3.4/go.mod h1:wCVwNJ/21W29FWFUv+fNawOTMlFoP1dS3L+ZuztFW48= github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k= github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -708,7 +706,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=