This commit is contained in:
Joe Adams 2025-08-05 15:22:56 -07:00 committed by GitHub
commit 7546734a29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 185 additions and 189 deletions

View File

@ -23,14 +23,14 @@ import (
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go/aws/awserr" awsConfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go/aws/session" ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/smithy-go"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config" "github.com/prometheus/common/config"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
@ -124,17 +124,22 @@ func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return err return err
} }
if c.Region == "" { if c.Region == "" {
sess, err := session.NewSession() cfg, err := awsConfig.LoadDefaultConfig(context.Background())
if err != nil { if err != nil {
return err return err
} }
metadata := ec2metadata.New(sess)
region, err := metadata.Region() if cfg.Region != "" {
if err != nil { // If the region is already set in the config, use it.
return errors.New("EC2 SD configuration requires a region") // 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 { for _, f := range c.Filters {
if len(f.Values) == 0 { if len(f.Values) == 0 {
return errors.New("EC2 SD configuration filter values cannot be empty") 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() 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 // EC2Discovery periodically performs EC2-SD requests. It implements
// the Discoverer interface. // the Discoverer interface.
type EC2Discovery struct { type EC2Discovery struct {
*refresh.Discovery *refresh.Discovery
logger *slog.Logger logger *slog.Logger
cfg *EC2SDConfig cfg *EC2SDConfig
ec2 ec2iface.EC2API ec2 ec2Client
// azToAZID maps this account's availability zones to their underlying AZ // azToAZID maps this account's availability zones to their underlying AZ
// ID, e.g. eu-west-2a -> euw2-az2. Refreshes are performed sequentially, so // 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 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 { if d.ec2 != nil {
return 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), "") // Build the HTTP client from the provided HTTPClientConfig.
if d.cfg.AccessKey == "" && d.cfg.SecretKey == "" { httpClient, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "ec2_sd")
creds = nil
}
client, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "ec2_sd")
if err != nil { if err != nil {
return nil, err return nil, err
} }
sess, err := session.NewSessionWithOptions(session.Options{ // Build the AWS config with the provided region and credentials.
Config: aws.Config{ cfg, err := awsConfig.LoadDefaultConfig(
Endpoint: &d.cfg.Endpoint, ctx,
Region: &d.cfg.Region, awsConfig.WithRegion(d.cfg.Region),
Credentials: creds, awsConfig.WithCredentialsProvider(credProvider),
HTTPClient: client, awsConfig.WithSharedConfigProfile(d.cfg.Profile),
}, awsConfig.WithHTTPClient(httpClient),
Profile: d.cfg.Profile, )
})
if err != nil { 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 != "" { if d.cfg.RoleARN != "" {
creds := stscreds.NewCredentials(sess, d.cfg.RoleARN) assumeProvider := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), d.cfg.RoleARN)
d.ec2 = ec2.New(sess, &aws.Config{Credentials: creds}) cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
} else {
d.ec2 = ec2.New(sess)
} }
d.ec2 = ec2.NewFromConfig(cfg)
return d.ec2, nil return d.ec2, nil
} }
func (d *EC2Discovery) refreshAZIDs(ctx context.Context) error { 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 { if err != nil {
return err return err
} }
@ -243,11 +250,11 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
Source: d.cfg.Region, Source: d.cfg.Region,
} }
var filters []*ec2.Filter var filters []ec2Types.Filter
for _, f := range d.cfg.Filters { for _, f := range d.cfg.Filters {
filters = append(filters, &ec2.Filter{ filters = append(filters, ec2Types.Filter{
Name: aws.String(f.Name), 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} 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 _, r := range p.Reservations {
for _, inst := range r.Instances { for _, inst := range r.Instances {
if inst.PrivateIpAddress == nil { 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)) addr := net.JoinHostPort(*inst.PrivateIpAddress, strconv.Itoa(d.cfg.Port))
labels[model.AddressLabel] = model.LabelValue(addr) labels[model.AddressLabel] = model.LabelValue(addr)
if inst.Platform != nil { if inst.Platform != "" {
labels[ec2LabelPlatform] = model.LabelValue(*inst.Platform) labels[ec2LabelPlatform] = model.LabelValue(inst.Platform)
} }
if inst.PublicIpAddress != nil { if inst.PublicIpAddress != nil {
@ -302,15 +320,15 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
"az", *inst.Placement.AvailabilityZone) "az", *inst.Placement.AvailabilityZone)
} }
labels[ec2LabelAZID] = model.LabelValue(azID) labels[ec2LabelAZID] = model.LabelValue(azID)
labels[ec2LabelInstanceState] = model.LabelValue(*inst.State.Name) labels[ec2LabelInstanceState] = model.LabelValue(inst.State.Name)
labels[ec2LabelInstanceType] = model.LabelValue(*inst.InstanceType) labels[ec2LabelInstanceType] = model.LabelValue(inst.InstanceType)
if inst.InstanceLifecycle != nil { if inst.InstanceLifecycle != "" {
labels[ec2LabelInstanceLifecycle] = model.LabelValue(*inst.InstanceLifecycle) labels[ec2LabelInstanceLifecycle] = model.LabelValue(inst.InstanceLifecycle)
} }
if inst.Architecture != nil { if inst.Architecture != "" {
labels[ec2LabelArch] = model.LabelValue(*inst.Architecture) labels[ec2LabelArch] = model.LabelValue(inst.Architecture)
} }
if inst.VpcId != nil { 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 // we might have to extend the slice with more than one element
// that could leave empty strings in the list which is intentional // that could leave empty strings in the list which is intentional
// to keep the position/device index information // 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 = append(primaryipv6addrs, "")
} }
primaryipv6addrs[*eni.Attachment.DeviceIndex] = *ipv6addr.Ipv6Address 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 { for _, t := range inst.Tags {
if t == nil || t.Key == nil || t.Value == nil { if t.Key == nil || t.Value == nil {
continue continue
} }
name := strutil.SanitizeLabelName(*t.Key) 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) 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 return []*targetgroup.Group{tg}, nil
} }

View File

@ -18,10 +18,9 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2" ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/goleak" "go.uber.org/goleak"
@ -39,10 +38,6 @@ func boolptr(b bool) *bool {
return &b return &b
} }
func int64ptr(i int64) *int64 {
return &i
}
// Struct for test data. // Struct for test data.
type ec2DataStore struct { type ec2DataStore struct {
region string region string
@ -51,7 +46,7 @@ type ec2DataStore struct {
ownerID string ownerID string
instances []*ec2.Instance instances []ec2Types.Instance
} }
// The tests itself. // The tests itself.
@ -121,7 +116,7 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
"azname-b": "azid-2", "azname-b": "azid-2",
"azname-c": "azid-3", "azname-c": "azid-3",
}, },
instances: []*ec2.Instance{ instances: []ec2Types.Instance{
{ {
InstanceId: strptr("instance-id-noprivateip"), InstanceId: strptr("instance-id-noprivateip"),
}, },
@ -143,26 +138,26 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
"azname-c": "azid-3", "azname-c": "azid-3",
}, },
ownerID: "owner-id-novpc", ownerID: "owner-id-novpc",
instances: []*ec2.Instance{ instances: []ec2Types.Instance{
{ {
// set every possible options and test them here // set every possible options and test them here
Architecture: strptr("architecture-novpc"), Architecture: "architecture-novpc",
ImageId: strptr("ami-novpc"), ImageId: strptr("ami-novpc"),
InstanceId: strptr("instance-id-novpc"), InstanceId: strptr("instance-id-novpc"),
InstanceLifecycle: strptr("instance-lifecycle-novpc"), InstanceLifecycle: "instance-lifecycle-novpc",
InstanceType: strptr("instance-type-novpc"), InstanceType: "instance-type-novpc",
Placement: &ec2.Placement{AvailabilityZone: strptr("azname-b")}, Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-b")},
Platform: strptr("platform-novpc"), Platform: "platform-novpc",
PrivateDnsName: strptr("private-dns-novpc"), PrivateDnsName: strptr("private-dns-novpc"),
PrivateIpAddress: strptr("1.2.3.4"), PrivateIpAddress: strptr("1.2.3.4"),
PublicDnsName: strptr("public-dns-novpc"), PublicDnsName: strptr("public-dns-novpc"),
PublicIpAddress: strptr("42.42.42.2"), PublicIpAddress: strptr("42.42.42.2"),
State: &ec2.InstanceState{Name: strptr("running")}, State: &ec2Types.InstanceState{Name: "running"},
// test tags once and for all // 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-1-key"), Value: strptr("tag-1-value")},
{Key: strptr("tag-2-key"), Value: strptr("tag-2-value")}, {Key: strptr("tag-2-key"), Value: strptr("tag-2-value")},
nil, {},
{Value: strptr("tag-4-value")}, {Value: strptr("tag-4-value")},
{Key: strptr("tag-5-key")}, {Key: strptr("tag-5-key")},
}, },
@ -206,22 +201,22 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
"azname-b": "azid-2", "azname-b": "azid-2",
"azname-c": "azid-3", "azname-c": "azid-3",
}, },
instances: []*ec2.Instance{ instances: []ec2Types.Instance{
{ {
// just the minimum needed for the refresh work // just the minimum needed for the refresh work
ImageId: strptr("ami-ipv4"), ImageId: strptr("ami-ipv4"),
InstanceId: strptr("instance-id-ipv4"), InstanceId: strptr("instance-id-ipv4"),
InstanceType: strptr("instance-type-ipv4"), InstanceType: "instance-type-ipv4",
Placement: &ec2.Placement{AvailabilityZone: strptr("azname-c")}, Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-c")},
PrivateIpAddress: strptr("5.6.7.8"), PrivateIpAddress: strptr("5.6.7.8"),
State: &ec2.InstanceState{Name: strptr("running")}, State: &ec2Types.InstanceState{Name: "running"},
SubnetId: strptr("azid-3"), SubnetId: strptr("azid-3"),
VpcId: strptr("vpc-ipv4"), VpcId: strptr("vpc-ipv4"),
// network interfaces // network interfaces
NetworkInterfaces: []*ec2.InstanceNetworkInterface{ NetworkInterfaces: []ec2Types.InstanceNetworkInterface{
// interface without subnet -> should be ignored // interface without subnet -> should be ignored
{ {
Ipv6Addresses: []*ec2.InstanceIpv6Address{ Ipv6Addresses: []ec2Types.InstanceIpv6Address{
{ {
Ipv6Address: strptr("2001:db8:1::1"), Ipv6Address: strptr("2001:db8:1::1"),
IsPrimaryIpv6: boolptr(true), IsPrimaryIpv6: boolptr(true),
@ -230,12 +225,12 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
}, },
// interface with subnet, no IPv6 // interface with subnet, no IPv6
{ {
Ipv6Addresses: []*ec2.InstanceIpv6Address{}, Ipv6Addresses: []ec2Types.InstanceIpv6Address{},
SubnetId: strptr("azid-3"), SubnetId: strptr("azid-3"),
}, },
// interface with another subnet, no IPv6 // interface with another subnet, no IPv6
{ {
Ipv6Addresses: []*ec2.InstanceIpv6Address{}, Ipv6Addresses: []ec2Types.InstanceIpv6Address{},
SubnetId: strptr("azid-1"), SubnetId: strptr("azid-1"),
}, },
}, },
@ -274,25 +269,25 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
"azname-b": "azid-2", "azname-b": "azid-2",
"azname-c": "azid-3", "azname-c": "azid-3",
}, },
instances: []*ec2.Instance{ instances: []ec2Types.Instance{
{ {
// just the minimum needed for the refresh work // just the minimum needed for the refresh work
ImageId: strptr("ami-ipv6"), ImageId: strptr("ami-ipv6"),
InstanceId: strptr("instance-id-ipv6"), InstanceId: strptr("instance-id-ipv6"),
InstanceType: strptr("instance-type-ipv6"), InstanceType: "instance-type-ipv6",
Placement: &ec2.Placement{AvailabilityZone: strptr("azname-b")}, Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-b")},
PrivateIpAddress: strptr("9.10.11.12"), PrivateIpAddress: strptr("9.10.11.12"),
State: &ec2.InstanceState{Name: strptr("running")}, State: &ec2Types.InstanceState{Name: "running"},
SubnetId: strptr("azid-2"), SubnetId: strptr("azid-2"),
VpcId: strptr("vpc-ipv6"), VpcId: strptr("vpc-ipv6"),
// network interfaces // network interfaces
NetworkInterfaces: []*ec2.InstanceNetworkInterface{ NetworkInterfaces: []ec2Types.InstanceNetworkInterface{
// interface without primary IPv6, index 2 // interface without primary IPv6, index 2
{ {
Attachment: &ec2.InstanceNetworkInterfaceAttachment{ Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{
DeviceIndex: int64ptr(3), DeviceIndex: aws.Int32(3),
}, },
Ipv6Addresses: []*ec2.InstanceIpv6Address{ Ipv6Addresses: []ec2Types.InstanceIpv6Address{
{ {
Ipv6Address: strptr("2001:db8:2::1:1"), Ipv6Address: strptr("2001:db8:2::1:1"),
IsPrimaryIpv6: boolptr(false), IsPrimaryIpv6: boolptr(false),
@ -302,10 +297,10 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
}, },
// interface with primary IPv6, index 1 // interface with primary IPv6, index 1
{ {
Attachment: &ec2.InstanceNetworkInterfaceAttachment{ Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{
DeviceIndex: int64ptr(1), DeviceIndex: aws.Int32(1),
}, },
Ipv6Addresses: []*ec2.InstanceIpv6Address{ Ipv6Addresses: []ec2Types.InstanceIpv6Address{
{ {
Ipv6Address: strptr("2001:db8:2::2:1"), Ipv6Address: strptr("2001:db8:2::2:1"),
IsPrimaryIpv6: boolptr(false), IsPrimaryIpv6: boolptr(false),
@ -319,10 +314,10 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
}, },
// interface with primary IPv6, index 3 // interface with primary IPv6, index 3
{ {
Attachment: &ec2.InstanceNetworkInterfaceAttachment{ Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{
DeviceIndex: int64ptr(3), DeviceIndex: aws.Int32(3),
}, },
Ipv6Addresses: []*ec2.InstanceIpv6Address{ Ipv6Addresses: []ec2Types.InstanceIpv6Address{
{ {
Ipv6Address: strptr("2001:db8:2::3:1"), Ipv6Address: strptr("2001:db8:2::3:1"),
IsPrimaryIpv6: boolptr(true), IsPrimaryIpv6: boolptr(true),
@ -332,10 +327,10 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
}, },
// interface without primary IPv6, index 0 // interface without primary IPv6, index 0
{ {
Attachment: &ec2.InstanceNetworkInterfaceAttachment{ Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{
DeviceIndex: int64ptr(0), DeviceIndex: aws.Int32(0),
}, },
Ipv6Addresses: []*ec2.InstanceIpv6Address{}, Ipv6Addresses: []ec2Types.InstanceIpv6Address{},
SubnetId: strptr("azid-3"), SubnetId: strptr("azid-3"),
}, },
}, },
@ -388,7 +383,6 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
// EC2 client mock. // EC2 client mock.
type mockEC2Client struct { type mockEC2Client struct {
ec2iface.EC2API
ec2Data ec2DataStore ec2Data ec2DataStore
} }
@ -399,16 +393,16 @@ func newMockEC2Client(ec2Data *ec2DataStore) *mockEC2Client {
return &client 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 { if len(m.ec2Data.azToAZID) == 0 {
return nil, errors.New("No AZs found") 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 i := 0
for k, v := range m.ec2Data.azToAZID { for k, v := range m.ec2Data.azToAZID {
azs[i] = &ec2.AvailabilityZone{ azs[i] = ec2Types.AvailabilityZone{
ZoneName: strptr(k), ZoneName: strptr(k),
ZoneId: strptr(v), ZoneId: strptr(v),
} }
@ -420,15 +414,13 @@ func (m *mockEC2Client) DescribeAvailabilityZonesWithContext(_ aws.Context, _ *e
}, nil }, nil
} }
func (m *mockEC2Client) DescribeInstancesPagesWithContext(_ aws.Context, _ *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, _ ...request.Option) error { func (m *mockEC2Client) DescribeInstances(_ context.Context, _ *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
r := ec2.Reservation{} r := ec2Types.Reservation{}
r.SetInstances(m.ec2Data.instances) r.Instances = append(r.Instances, m.ec2Data.instances...)
r.SetOwnerId(m.ec2Data.ownerID) r.OwnerId = aws.String(m.ec2Data.ownerID)
o := ec2.DescribeInstancesOutput{} o := ec2.DescribeInstancesOutput{}
o.SetReservations([]*ec2.Reservation{&r}) o.Reservations = []ec2Types.Reservation{r}
_ = fn(&o, true) return &o, nil
return nil
} }

View File

@ -23,13 +23,13 @@ import (
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go/aws/awserr" awsConfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go-v2/service/lightsail"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/aws-sdk-go/service/lightsail" "github.com/aws/smithy-go"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config" "github.com/prometheus/common/config"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
@ -106,20 +106,19 @@ func (c *LightsailSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
return err return err
} }
if c.Region == "" { if c.Region == "" {
sess, err := session.NewSession() cfg, err := awsConfig.LoadDefaultConfig(context.Background())
if err != nil { if err != nil {
return err return err
} }
metadata := ec2metadata.New(sess) // Use the region from the AWS config. It will load environment variables and shared config files.
c.Region = cfg.Region
region, err := metadata.Region()
if err != nil {
//nolint:staticcheck // Capitalized first word.
return errors.New("Lightsail SD configuration requires a region")
}
c.Region = region
} }
if c.Region == "" {
return errors.New("lightsail SD configuration requires a region")
}
return c.HTTPClientConfig.Validate() return c.HTTPClientConfig.Validate()
} }
@ -128,7 +127,7 @@ func (c *LightsailSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
type LightsailDiscovery struct { type LightsailDiscovery struct {
*refresh.Discovery *refresh.Discovery
cfg *LightsailSDConfig cfg *LightsailSDConfig
lightsail *lightsail.Lightsail lightsail *lightsail.Client
} }
// NewLightsailDiscovery returns a new LightsailDiscovery which periodically refreshes its targets. // 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 return d, nil
} }
func (d *LightsailDiscovery) lightsailClient() (*lightsail.Lightsail, error) { func (d *LightsailDiscovery) lightsailClient(ctx context.Context) (*lightsail.Client, error) {
if d.lightsail != nil { if d.lightsail != nil {
return d.lightsail, nil return d.lightsail, nil
} }
creds := credentials.NewStaticCredentials(d.cfg.AccessKey, string(d.cfg.SecretKey), "") credProvider := credentials.NewStaticCredentialsProvider(d.cfg.AccessKey, string(d.cfg.SecretKey), "")
if d.cfg.AccessKey == "" && d.cfg.SecretKey == "" {
creds = nil
}
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 { if err != nil {
return nil, err return nil, err
} }
sess, err := session.NewSessionWithOptions(session.Options{ // Build the AWS config with the provided region and credentials.
Config: aws.Config{ cfg, err := awsConfig.LoadDefaultConfig(
Endpoint: &d.cfg.Endpoint, ctx,
Region: &d.cfg.Region, awsConfig.WithRegion(d.cfg.Region),
Credentials: creds, awsConfig.WithCredentialsProvider(credProvider),
HTTPClient: client, awsConfig.WithSharedConfigProfile(d.cfg.Profile),
}, awsConfig.WithHTTPClient(httpClient),
Profile: d.cfg.Profile, )
})
if err != nil { 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 != "" { if d.cfg.RoleARN != "" {
creds := stscreds.NewCredentials(sess, d.cfg.RoleARN) assumeProvider := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), d.cfg.RoleARN)
d.lightsail = lightsail.New(sess, &aws.Config{Credentials: creds}) cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
} else {
d.lightsail = lightsail.New(sess)
} }
d.lightsail = lightsail.NewFromConfig(cfg)
return d.lightsail, nil return d.lightsail, nil
} }
func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
lightsailClient, err := d.lightsailClient() lightsailClient, err := d.lightsailClient(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -207,10 +204,10 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
input := &lightsail.GetInstancesInput{} input := &lightsail.GetInstancesInput{}
output, err := lightsailClient.GetInstancesWithContext(ctx, input) output, err := lightsailClient.GetInstances(ctx, input)
if err != nil { if err != nil {
var awsErr awserr.Error var awsErr smithy.APIError
if errors.As(err, &awsErr) && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") { if errors.As(err, &awsErr) && (awsErr.ErrorCode() == "AuthFailure" || awsErr.ErrorCode() == "UnauthorizedOperation") {
d.lightsail = nil d.lightsail = nil
} }
return nil, fmt.Errorf("could not get instances: %w", err) 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 { if len(inst.Ipv6Addresses) > 0 {
var ipv6addrs []string var ipv6addrs []string
for _, ipv6addr := range inst.Ipv6Addresses { ipv6addrs = append(ipv6addrs, inst.Ipv6Addresses...)
ipv6addrs = append(ipv6addrs, *ipv6addr)
}
labels[lightsailLabelIPv6Addresses] = model.LabelValue( labels[lightsailLabelIPv6Addresses] = model.LabelValue(
lightsailLabelSeparator + lightsailLabelSeparator +
strings.Join(ipv6addrs, lightsailLabelSeparator) + strings.Join(ipv6addrs, lightsailLabelSeparator) +
@ -251,7 +246,7 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
} }
for _, t := range inst.Tags { for _, t := range inst.Tags {
if t == nil || t.Key == nil || t.Value == nil { if t.Key == nil || t.Value == nil {
continue continue
} }
name := strutil.SanitizeLabelName(*t.Key) name := strutil.SanitizeLabelName(*t.Key)

22
go.mod
View File

@ -11,7 +11,13 @@ require (
github.com/KimMachineGun/automemlimit v0.7.3 github.com/KimMachineGun/automemlimit v0.7.3
github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/kingpin/v2 v2.4.0
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b 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/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3
github.com/cespare/xxhash/v2 v2.3.0 github.com/cespare/xxhash/v2 v2.3.0
github.com/dennwc/varint v1.0.0 github.com/dennwc/varint v1.0.0
@ -95,19 +101,14 @@ require (
) )
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/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/configsources v1.4.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // 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/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/accept-encoding v1.13.0 // 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/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/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/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/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.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/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect
github.com/hashicorp/serf v0.10.1 // 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/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect

35
go.sum
View File

@ -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/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 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 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-v2 v1.37.0 h1:YtCOESR/pN4j5oA7cVHSfOwIcuh/KwHC4DOSXFbv5F0=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.37.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
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/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= 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/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 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/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 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/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.4.0 h1:H2iZoqW/v2Jnrh1FnU725Bq6KJ0k2uP63yH+DcY+HUI=
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/configsources v1.4.0/go.mod h1:L0FqLbwMXHvNC/7crWV1iIxUlOKYZUE8KuTIA+TozAI=
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.7.0 h1:EDped/rNzAhFPhVY0sDGbtD16OKqksfA8OjF/kLEgw8=
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/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 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/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/ec2 v1.237.0 h1:XHE2G+yaDQql32FZt19QmQt4WuisqQJIkMUSCxeCUl8=
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/ec2 v1.237.0/go.mod h1:t11/j/nH9i6bbsPH9xc04BJOsV2nVPUqrB67/TLDsyM=
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/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
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/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 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/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 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/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 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= 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.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= 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 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps=
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= 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= 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/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 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= 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 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 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.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.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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=