@ -28,6 +28,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
@ -42,6 +43,7 @@ const (
ec2Label = model . MetaLabelPrefix + "ec2_"
ec2LabelAMI = ec2Label + "ami"
ec2LabelAZ = ec2Label + "availability_zone"
ec2LabelAZID = ec2Label + "availability_zone_id"
ec2LabelArch = ec2Label + "architecture"
ec2LabelIPv6Addresses = ec2Label + "ipv6_addresses"
ec2LabelInstanceID = ec2Label + "instance_id"
@ -132,8 +134,14 @@ func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
// the Discoverer interface.
type EC2Discovery struct {
* refresh . Discovery
cfg * EC2SDConfig
ec2 * ec2 . EC2
logger log . Logger
cfg * EC2SDConfig
ec2 * ec2 . EC2
// azToAZID maps this account's availability zones to their underlying AZ
// ID, e.g. eu-west-2a -> euw2-az2. Refreshes are performed sequentially, so
// no locking is required.
azToAZID map [ string ] string
}
// NewEC2Discovery returns a new EC2Discovery which periodically refreshes its targets.
@ -142,7 +150,8 @@ func NewEC2Discovery(conf *EC2SDConfig, logger log.Logger) *EC2Discovery {
logger = log . NewNopLogger ( )
}
d := & EC2Discovery {
cfg : conf ,
logger : logger ,
cfg : conf ,
}
d . Discovery = refresh . NewDiscovery (
logger ,
@ -153,7 +162,7 @@ func NewEC2Discovery(conf *EC2SDConfig, logger log.Logger) *EC2Discovery {
return d
}
func ( d * EC2Discovery ) ec2Client ( ) ( * ec2 . EC2 , error ) {
func ( d * EC2Discovery ) ec2Client ( ctx context . Context ) ( * ec2 . EC2 , error ) {
if d . ec2 != nil {
return d . ec2 , nil
}
@ -185,8 +194,28 @@ func (d *EC2Discovery) ec2Client() (*ec2.EC2, error) {
return d . ec2 , nil
}
func ( d * EC2Discovery ) azID ( ctx context . Context , az string ) ( string , error ) {
if azID , ok := d . azToAZID [ az ] ; ok {
return azID , nil
}
azs , err := d . ec2 . DescribeAvailabilityZonesWithContext ( ctx , & ec2 . DescribeAvailabilityZonesInput { } )
if err != nil {
return "" , errors . Wrap ( err , "could not describe availability zones" )
}
d . azToAZID = make ( map [ string ] string , len ( azs . AvailabilityZones ) )
for _ , az := range azs . AvailabilityZones {
d . azToAZID [ * az . ZoneName ] = * az . ZoneId
}
if azID , ok := d . azToAZID [ az ] ; ok {
return azID , nil
}
return "" , fmt . Errorf ( "no availability zone ID mapping found for %s" , az )
}
func ( d * EC2Discovery ) refresh ( ctx context . Context ) ( [ ] * targetgroup . Group , error ) {
ec2Client , err := d . ec2Client ( )
ec2Client , err := d . ec2Client ( ctx )
if err != nil {
return nil , err
}
@ -211,6 +240,14 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
if inst . PrivateIpAddress == nil {
continue
}
azID , err := d . azID ( ctx , * inst . Placement . AvailabilityZone )
if err != nil {
level . Warn ( d . logger ) . Log (
"msg" , "Unable to determine availability zone ID" ,
"az" , * inst . Placement . AvailabilityZone ,
"err" , err )
}
labels := model . LabelSet {
ec2LabelInstanceID : model . LabelValue ( * inst . InstanceId ) ,
}
@ -237,6 +274,7 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
labels [ ec2LabelAMI ] = model . LabelValue ( * inst . ImageId )
labels [ ec2LabelAZ ] = model . LabelValue ( * inst . Placement . AvailabilityZone )
labels [ ec2LabelAZID ] = model . LabelValue ( azID )
labels [ ec2LabelInstanceState ] = model . LabelValue ( * inst . State . Name )
labels [ ec2LabelInstanceType ] = model . LabelValue ( * inst . InstanceType )