diff --git a/config/config_test.go b/config/config_test.go index af58ebd20f..c7061e8547 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -412,6 +412,16 @@ var expectedConf = &Config{ Profile: "profile", RefreshInterval: model.Duration(60 * time.Second), Port: 80, + Filters: []*ec2.Filter{ + { + Name: "tag:environment", + Values: []string{"prod"}, + }, + { + Name: "tag:service", + Values: []string{"web", "db"}, + }, + }, }, }, }, @@ -688,6 +698,10 @@ var expectedErrors = []struct { filename: "remote_write_url_missing.bad.yml", errMsg: `url for remote_write is empty`, }, + { + filename: "ec2_filters_empty_values.bad.yml", + errMsg: `EC2 SD configuration filter values cannot be empty`, + }, } func TestBadConfigs(t *testing.T) { diff --git a/config/testdata/conf.good.yml b/config/testdata/conf.good.yml index 9ad55d5a6d..fa4938a622 100644 --- a/config/testdata/conf.good.yml +++ b/config/testdata/conf.good.yml @@ -182,6 +182,15 @@ scrape_configs: access_key: access secret_key: mysecret profile: profile + filters: + - name: tag:environment + values: + - prod + + - name: tag:service + values: + - web + - db - job_name: service-azure azure_sd_configs: diff --git a/config/testdata/ec2_filters_empty_values.bad.yml b/config/testdata/ec2_filters_empty_values.bad.yml new file mode 100644 index 0000000000..f375bf5649 --- /dev/null +++ b/config/testdata/ec2_filters_empty_values.bad.yml @@ -0,0 +1,9 @@ +scrape_configs: + - job_name: prometheus + + ec2_sd_configs: + - region: 'us-east-1' + filters: + - name: 'tag:environment' + values: + diff --git a/discovery/ec2/ec2.go b/discovery/ec2/ec2.go index aba03066c2..4a35b1f4c5 100644 --- a/discovery/ec2/ec2.go +++ b/discovery/ec2/ec2.go @@ -70,6 +70,12 @@ var ( } ) +// Filter is the configuration for filtering EC2 instances. +type Filter struct { + Name string `yaml:"name"` + Values []string `yaml:"values"` +} + // SDConfig is the configuration for EC2 based service discovery. type SDConfig struct { Region string `yaml:"region"` @@ -79,6 +85,7 @@ type SDConfig struct { RoleARN string `yaml:"role_arn,omitempty"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` Port int `yaml:"port"` + Filters []*Filter `yaml:"filters"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` @@ -107,6 +114,11 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } c.Region = region } + for _, f := range c.Filters { + if len(f.Values) == 0 { + return fmt.Errorf("EC2 SD configuration filter values cannot be empty") + } + } return nil } @@ -123,6 +135,7 @@ type Discovery struct { profile string roleARN string port int + filters []*Filter logger log.Logger } @@ -142,6 +155,7 @@ func NewDiscovery(conf *SDConfig, logger log.Logger) *Discovery { }, profile: conf.Profile, roleARN: conf.RoleARN, + filters: conf.Filters, interval: time.Duration(conf.RefreshInterval), port: conf.Port, logger: logger, @@ -212,7 +226,18 @@ func (d *Discovery) refresh() (tg *targetgroup.Group, err error) { tg = &targetgroup.Group{ Source: *d.aws.Region, } - if err = ec2s.DescribeInstancesPages(nil, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool { + + var filters []*ec2.Filter + for _, f := range d.filters { + filters = append(filters, &ec2.Filter{ + Name: aws.String(f.Name), + Values: aws.StringSlice(f.Values), + }) + } + + input := &ec2.DescribeInstancesInput{Filters: filters} + + if err = ec2s.DescribeInstancesPages(input, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool { for _, r := range p.Reservations { for _, inst := range r.Instances { if inst.PrivateIpAddress == nil { diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index de7b59573e..59418078b5 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -429,8 +429,21 @@ region: # The port to scrape metrics from. If using the public IP address, this must # instead be specified in the relabeling rule. [ port: | default = 80 ] + +# Filters can be used optionally to filter the instance list by other criteria. +# Available filter criteria can be found here: +# https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html +# Filter API documentation: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_Filter.html +filters: + [ - name: + values: , [...] ] ``` +The [relabeling phase](#relabel_config) is the preferred and more powerful +way to filter targets based on arbitrary labels. For users with thousands of +instances it can be more efficient to use the EC2 API directly which has +support for filtering instances. + ### `` OpenStack SD configurations allow retrieving scrape targets from OpenStack Nova