EC2 SD: fix error handling of NewSessionWithOptions (#8356)

Last change in 4efca5a introduced a problem where NewDiscovery would
just return a nil value, which is not handled well and didn't allow for
fixing configuration issues at runtime without a reload.

Signed-off-by: Alfred Krohmer <alfred.krohmer@logmein.com>
pull/8412/head
Alfred Krohmer 4 years ago committed by GitHub
parent 02ee690e3c
commit fc8004eeec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws" "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"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/ec2metadata"
@ -129,66 +130,71 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
// the Discoverer interface. // the Discoverer interface.
type Discovery struct { type Discovery struct {
*refresh.Discovery *refresh.Discovery
region string cfg *SDConfig
interval time.Duration
port int
filters []*Filter
ec2 *ec2.EC2 ec2 *ec2.EC2
} }
// NewDiscovery returns a new EC2Discovery which periodically refreshes its targets. // NewDiscovery returns a new EC2Discovery which periodically refreshes its targets.
func NewDiscovery(conf *SDConfig, logger log.Logger) *Discovery { func NewDiscovery(conf *SDConfig, logger log.Logger) *Discovery {
creds := credentials.NewStaticCredentials(conf.AccessKey, string(conf.SecretKey), "")
if conf.AccessKey == "" && conf.SecretKey == "" {
creds = nil
}
if logger == nil { if logger == nil {
logger = log.NewNopLogger() logger = log.NewNopLogger()
} }
d := &Discovery{
cfg: conf,
}
d.Discovery = refresh.NewDiscovery(
logger,
"ec2",
time.Duration(d.cfg.RefreshInterval),
d.refresh,
)
return d
}
func (d *Discovery) ec2Client() (*ec2.EC2, error) {
if d.ec2 != nil {
return d.ec2, nil
}
creds := credentials.NewStaticCredentials(d.cfg.AccessKey, string(d.cfg.SecretKey), "")
if d.cfg.AccessKey == "" && d.cfg.SecretKey == "" {
creds = nil
}
sess, err := session.NewSessionWithOptions(session.Options{ sess, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{ Config: aws.Config{
Endpoint: &conf.Endpoint, Endpoint: &d.cfg.Endpoint,
Region: &conf.Region, Region: &d.cfg.Region,
Credentials: creds, Credentials: creds,
}, },
Profile: conf.Profile, Profile: d.cfg.Profile,
}) })
if err != nil { if err != nil {
return nil return nil, errors.Wrap(err, "could not create aws session")
} }
var ec2s *ec2.EC2 if d.cfg.RoleARN != "" {
if conf.RoleARN != "" { creds := stscreds.NewCredentials(sess, d.cfg.RoleARN)
creds := stscreds.NewCredentials(sess, conf.RoleARN) d.ec2 = ec2.New(sess, &aws.Config{Credentials: creds})
ec2s = ec2.New(sess, &aws.Config{Credentials: creds})
} else { } else {
ec2s = ec2.New(sess) d.ec2 = ec2.New(sess)
} }
d := &Discovery{ return d.ec2, nil
region: conf.Region,
filters: conf.Filters,
interval: time.Duration(conf.RefreshInterval),
port: conf.Port,
ec2: ec2s,
}
d.Discovery = refresh.NewDiscovery(
logger,
"ec2",
time.Duration(conf.RefreshInterval),
d.refresh,
)
return d
} }
func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
ec2Client, err := d.ec2Client()
if err != nil {
return nil, err
}
tg := &targetgroup.Group{ tg := &targetgroup.Group{
Source: d.region, Source: d.cfg.Region,
} }
var filters []*ec2.Filter var filters []*ec2.Filter
for _, f := range d.filters { for _, f := range d.cfg.Filters {
filters = append(filters, &ec2.Filter{ filters = append(filters, &ec2.Filter{
Name: aws.String(f.Name), Name: aws.String(f.Name),
Values: aws.StringSlice(f.Values), Values: aws.StringSlice(f.Values),
@ -197,7 +203,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
input := &ec2.DescribeInstancesInput{Filters: filters} input := &ec2.DescribeInstancesInput{Filters: filters}
if err := d.ec2.DescribeInstancesPagesWithContext(ctx, input, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool { if err := ec2Client.DescribeInstancesPagesWithContext(ctx, input, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool {
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 {
@ -215,7 +221,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
if inst.PrivateDnsName != nil { if inst.PrivateDnsName != nil {
labels[ec2LabelPrivateDNS] = model.LabelValue(*inst.PrivateDnsName) labels[ec2LabelPrivateDNS] = model.LabelValue(*inst.PrivateDnsName)
} }
addr := net.JoinHostPort(*inst.PrivateIpAddress, fmt.Sprintf("%d", d.port)) addr := net.JoinHostPort(*inst.PrivateIpAddress, fmt.Sprintf("%d", d.cfg.Port))
labels[model.AddressLabel] = model.LabelValue(addr) labels[model.AddressLabel] = model.LabelValue(addr)
if inst.Platform != nil { if inst.Platform != nil {
@ -285,6 +291,9 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
} }
return true return true
}); err != nil { }); err != nil {
if awsErr, ok := err.(awserr.Error); ok && (awsErr.Code() == "AuthFailure" || awsErr.Code() == "UnauthorizedOperation") {
d.ec2 = nil
}
return nil, errors.Wrap(err, "could not describe instances") return nil, errors.Wrap(err, "could not describe instances")
} }
return []*targetgroup.Group{tg}, nil return []*targetgroup.Group{tg}, nil

Loading…
Cancel
Save