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"
"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
}

View File

@ -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
}

View File

@ -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)

22
go.mod
View File

@ -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

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/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=