Skip to content

Commit 7a3266d

Browse files
authored
Implement Transit gateway metrics and ec2 exporter (#46)
* implement Transit gateway metrics and ec2 exporter * Re-enable other exporters * Replace context.TODO so timeouts are respected * * add missing log statement * update docs * change name of transitgateway metric to be in line with others
1 parent c800b8a commit 7a3266d

File tree

4 files changed

+154
-2
lines changed

4 files changed

+154
-2
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This was made as a complement to [CloudWatch Exporter](https://github.com/promet
2121
| VPC | routetablespervpc | Quota and usage of routetables per VPC |
2222
| VPC | routesperroutetable | Quota and usage of the routes per routetable |
2323
| VPC | ipv4blockspervpc | Quota and usage of ipv4 blocks per VPC |
24+
| EC2 | transitgatewaysperregion | Quota and usage of transitgateways per region |
2425
| Route53 | recordsperhostedzone | Quota and usage of resource records per Hosted Zone |
2526

2627

@@ -71,7 +72,13 @@ vpc:
7172
regions:
7273
- "us-east-1"
7374
- "eu-central-1"
74-
- "eu-central-2"
75+
timeout: 30s
76+
ec2:
77+
enabled: true
78+
regions:
79+
- "us-east-1"
80+
- "eu-central-1"
81+
- "us-west-1"
7582
timeout: 30s
7683
route53:
7784
enabled: true

aws-resource-exporter-config.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ vpc:
77
regions:
88
- "us-east-1"
99
- "eu-central-1"
10-
- "eu-central-2"
1110
timeout: 30s
1211
route53:
1312
enabled: true
1413
region: "us-east-1"
1514
timeout: 60s
15+
ec2:
16+
enabled: true
17+
regions:
18+
- "us-east-1"
19+
- "eu-central-1"
20+
- "us-west-1"
21+
timeout: 30s

ec2.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
"github.com/aws/aws-sdk-go/aws"
9+
"github.com/aws/aws-sdk-go/aws/session"
10+
"github.com/aws/aws-sdk-go/service/ec2"
11+
"github.com/aws/aws-sdk-go/service/servicequotas"
12+
"github.com/go-kit/kit/log"
13+
"github.com/go-kit/kit/log/level"
14+
"github.com/prometheus/client_golang/prometheus"
15+
)
16+
17+
const (
18+
transitGatewayPerAccountQuotaCode string = "L-A2478D36"
19+
ec2ServiceCode string = "ec2"
20+
)
21+
22+
var TransitGatewaysQuota *prometheus.Desc = prometheus.NewDesc(prometheus.BuildFQName(namespace, "", "ec2_transitgatewaysperregion_quota"), "Quota for maximum number of Transitgateways in this account", []string{"aws_region"}, nil)
23+
var TransitGatewaysUsage *prometheus.Desc = prometheus.NewDesc(prometheus.BuildFQName(namespace, "", "ec2_transitgatewaysperregion_usage"), "Number of Tranitgatewyas in the AWS Account", []string{"aws_region"}, nil)
24+
25+
type EC2Exporter struct {
26+
sessions []*session.Session
27+
28+
logger log.Logger
29+
timeout time.Duration
30+
}
31+
32+
func NewEC2Exporter(sessions []*session.Session, logger log.Logger, timeout time.Duration) *EC2Exporter {
33+
34+
level.Info(logger).Log("msg", "Initializing EC2 exporter")
35+
return &EC2Exporter{
36+
sessions: sessions,
37+
38+
logger: logger,
39+
timeout: timeout,
40+
}
41+
}
42+
43+
func (e *EC2Exporter) Collect(ch chan<- prometheus.Metric) {
44+
ctx, ctxCancel := context.WithTimeout(context.Background(), e.timeout)
45+
defer ctxCancel()
46+
wg := &sync.WaitGroup{}
47+
wg.Add(len(e.sessions))
48+
49+
for _, sess := range e.sessions {
50+
go collectInRegion(sess, e.logger, wg, ch, ctx)
51+
}
52+
wg.Wait()
53+
}
54+
55+
func collectInRegion(sess *session.Session, logger log.Logger, wg *sync.WaitGroup, ch chan<- prometheus.Metric, ctx context.Context) {
56+
defer wg.Done()
57+
ec2Svc := ec2.New(sess)
58+
serviceQuotaSvc := servicequotas.New(sess)
59+
60+
quota, err := getQuotaValueWithContext(serviceQuotaSvc, ec2ServiceCode, transitGatewayPerAccountQuotaCode, ctx)
61+
if err != nil {
62+
level.Error(logger).Log("msg", "Could not retrieve Transit Gateway quota", "error", err.Error())
63+
exporterMetrics.IncrementErrors()
64+
return
65+
}
66+
67+
gateways, err := getAllTransitGatewaysWithContext(ec2Svc, ctx)
68+
if err != nil {
69+
level.Error(logger).Log("msg", "Could not retrieve Transit Gateway quota", "error", err.Error())
70+
exporterMetrics.IncrementErrors()
71+
return
72+
}
73+
74+
ch <- prometheus.MustNewConstMetric(TransitGatewaysUsage, prometheus.GaugeValue, float64(len(gateways)), *sess.Config.Region)
75+
ch <- prometheus.MustNewConstMetric(TransitGatewaysQuota, prometheus.GaugeValue, quota, *sess.Config.Region)
76+
77+
}
78+
79+
func (e *EC2Exporter) Describe(ch chan<- *prometheus.Desc) {
80+
ch <- TransitGatewaysQuota
81+
ch <- TransitGatewaysUsage
82+
}
83+
84+
func getAllTransitGatewaysWithContext(client *ec2.EC2, ctx context.Context) ([]*ec2.TransitGateway, error) {
85+
results := []*ec2.TransitGateway{}
86+
describeGatewaysInput := &ec2.DescribeTransitGatewaysInput{
87+
DryRun: aws.Bool(false),
88+
MaxResults: aws.Int64(1000),
89+
}
90+
91+
describeGatewaysOutput, err := client.DescribeTransitGatewaysWithContext(ctx, describeGatewaysInput)
92+
93+
if err != nil {
94+
return nil, err
95+
}
96+
results = append(results, describeGatewaysOutput.TransitGateways...)
97+
98+
for describeGatewaysOutput.NextToken != nil {
99+
describeGatewaysInput.SetNextToken(*describeGatewaysOutput.NextToken)
100+
describeGatewaysOutput, err := client.DescribeTransitGatewaysWithContext(ctx, describeGatewaysInput)
101+
if err != nil {
102+
return nil, err
103+
}
104+
results = append(results, describeGatewaysOutput.TransitGateways...)
105+
}
106+
107+
return results, nil
108+
}
109+
110+
func getQuotaValueWithContext(client *servicequotas.ServiceQuotas, serviceCode string, quotaCode string, ctx context.Context) (float64, error) {
111+
sqOutput, err := client.GetServiceQuotaWithContext(ctx, &servicequotas.GetServiceQuotaInput{
112+
QuotaCode: aws.String(quotaCode),
113+
ServiceCode: aws.String(serviceCode),
114+
})
115+
116+
if err != nil {
117+
return 0, err
118+
}
119+
120+
return *sqOutput.Quota.Value, nil
121+
}

main.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,17 @@ type Route53Config struct {
6363
Region string `yaml:"region"` // Use only a single Region for now, as the current metric is global
6464
}
6565

66+
type EC2Config struct {
67+
BaseConfig `yaml:"base,inline"`
68+
Timeout time.Duration `yaml:"timeout"`
69+
Regions []string `yaml:"regions"`
70+
}
71+
6672
type Config struct {
6773
RdsConfig RDSConfig `yaml:"rds"`
6874
VpcConfig VPCConfig `yaml:"vpc"`
6975
Route53Config Route53Config `yaml:"route53"`
76+
EC2Config EC2Config `yaml:"ec2"`
7077
}
7178

7279
func loadExporterConfiguration(logger log.Logger, configFile string) (*Config, error) {
@@ -88,6 +95,7 @@ func setupCollectors(logger log.Logger, configFile string, creds *credentials.Cr
8895
}
8996
level.Info(logger).Log("msg", "Configuring vpc with regions", "regions", strings.Join(config.VpcConfig.Regions, ","))
9097
level.Info(logger).Log("msg", "Configuring rds with regions", "regions", strings.Join(config.RdsConfig.Regions, ","))
98+
level.Info(logger).Log("msg", "Configuring ec2 with regions", "regions", strings.Join(config.EC2Config.Regions, ","))
9199
level.Info(logger).Log("msg", "Configuring route53 with region", "region", config.Route53Config.Region)
92100
var vpcSessions []*session.Session
93101
level.Info(logger).Log("msg", "Will VPC metrics be gathered?", "vpc-enabled", config.VpcConfig.Enabled)
@@ -109,6 +117,16 @@ func setupCollectors(logger log.Logger, configFile string, creds *credentials.Cr
109117
}
110118
collectors = append(collectors, NewRDSExporter(rdsSessions, logger))
111119
}
120+
level.Info(logger).Log("msg", "Will EC2 metrics be gathered?", "ec2-enabled", config.EC2Config.Enabled)
121+
var ec2Sessions []*session.Session
122+
if config.EC2Config.Enabled {
123+
for _, region := range config.EC2Config.Regions {
124+
config := aws.NewConfig().WithCredentials(creds).WithRegion(region)
125+
sess := session.Must(session.NewSession(config))
126+
ec2Sessions = append(ec2Sessions, sess)
127+
}
128+
collectors = append(collectors, NewEC2Exporter(ec2Sessions, logger, config.EC2Config.Timeout))
129+
}
112130
level.Info(logger).Log("msg", "Will Route53 metrics be gathered?", "route53-enabled", config.Route53Config.Enabled)
113131
if config.Route53Config.Enabled {
114132
awsConfig := aws.NewConfig().WithCredentials(creds).WithRegion(config.Route53Config.Region)

0 commit comments

Comments
 (0)