Skip to content

Commit

Permalink
Implement Transit gateway metrics and ec2 exporter (#46)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mrWinston authored Aug 24, 2022
1 parent c800b8a commit 7a3266d
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 2 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This was made as a complement to [CloudWatch Exporter](https://github.com/promet
| VPC | routetablespervpc | Quota and usage of routetables per VPC |
| VPC | routesperroutetable | Quota and usage of the routes per routetable |
| VPC | ipv4blockspervpc | Quota and usage of ipv4 blocks per VPC |
| EC2 | transitgatewaysperregion | Quota and usage of transitgateways per region |
| Route53 | recordsperhostedzone | Quota and usage of resource records per Hosted Zone |


Expand Down Expand Up @@ -71,7 +72,13 @@ vpc:
regions:
- "us-east-1"
- "eu-central-1"
- "eu-central-2"
timeout: 30s
ec2:
enabled: true
regions:
- "us-east-1"
- "eu-central-1"
- "us-west-1"
timeout: 30s
route53:
enabled: true
Expand Down
8 changes: 7 additions & 1 deletion aws-resource-exporter-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ vpc:
regions:
- "us-east-1"
- "eu-central-1"
- "eu-central-2"
timeout: 30s
route53:
enabled: true
region: "us-east-1"
timeout: 60s
ec2:
enabled: true
regions:
- "us-east-1"
- "eu-central-1"
- "us-west-1"
timeout: 30s
121 changes: 121 additions & 0 deletions ec2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package main

import (
"context"
"sync"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/servicequotas"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus"
)

const (
transitGatewayPerAccountQuotaCode string = "L-A2478D36"
ec2ServiceCode string = "ec2"
)

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)
var TransitGatewaysUsage *prometheus.Desc = prometheus.NewDesc(prometheus.BuildFQName(namespace, "", "ec2_transitgatewaysperregion_usage"), "Number of Tranitgatewyas in the AWS Account", []string{"aws_region"}, nil)

type EC2Exporter struct {
sessions []*session.Session

logger log.Logger
timeout time.Duration
}

func NewEC2Exporter(sessions []*session.Session, logger log.Logger, timeout time.Duration) *EC2Exporter {

level.Info(logger).Log("msg", "Initializing EC2 exporter")
return &EC2Exporter{
sessions: sessions,

logger: logger,
timeout: timeout,
}
}

func (e *EC2Exporter) Collect(ch chan<- prometheus.Metric) {
ctx, ctxCancel := context.WithTimeout(context.Background(), e.timeout)
defer ctxCancel()
wg := &sync.WaitGroup{}
wg.Add(len(e.sessions))

for _, sess := range e.sessions {
go collectInRegion(sess, e.logger, wg, ch, ctx)
}
wg.Wait()
}

func collectInRegion(sess *session.Session, logger log.Logger, wg *sync.WaitGroup, ch chan<- prometheus.Metric, ctx context.Context) {
defer wg.Done()
ec2Svc := ec2.New(sess)
serviceQuotaSvc := servicequotas.New(sess)

quota, err := getQuotaValueWithContext(serviceQuotaSvc, ec2ServiceCode, transitGatewayPerAccountQuotaCode, ctx)
if err != nil {
level.Error(logger).Log("msg", "Could not retrieve Transit Gateway quota", "error", err.Error())
exporterMetrics.IncrementErrors()
return
}

gateways, err := getAllTransitGatewaysWithContext(ec2Svc, ctx)
if err != nil {
level.Error(logger).Log("msg", "Could not retrieve Transit Gateway quota", "error", err.Error())
exporterMetrics.IncrementErrors()
return
}

ch <- prometheus.MustNewConstMetric(TransitGatewaysUsage, prometheus.GaugeValue, float64(len(gateways)), *sess.Config.Region)
ch <- prometheus.MustNewConstMetric(TransitGatewaysQuota, prometheus.GaugeValue, quota, *sess.Config.Region)

}

func (e *EC2Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- TransitGatewaysQuota
ch <- TransitGatewaysUsage
}

func getAllTransitGatewaysWithContext(client *ec2.EC2, ctx context.Context) ([]*ec2.TransitGateway, error) {
results := []*ec2.TransitGateway{}
describeGatewaysInput := &ec2.DescribeTransitGatewaysInput{
DryRun: aws.Bool(false),
MaxResults: aws.Int64(1000),
}

describeGatewaysOutput, err := client.DescribeTransitGatewaysWithContext(ctx, describeGatewaysInput)

if err != nil {
return nil, err
}
results = append(results, describeGatewaysOutput.TransitGateways...)

for describeGatewaysOutput.NextToken != nil {
describeGatewaysInput.SetNextToken(*describeGatewaysOutput.NextToken)
describeGatewaysOutput, err := client.DescribeTransitGatewaysWithContext(ctx, describeGatewaysInput)
if err != nil {
return nil, err
}
results = append(results, describeGatewaysOutput.TransitGateways...)
}

return results, nil
}

func getQuotaValueWithContext(client *servicequotas.ServiceQuotas, serviceCode string, quotaCode string, ctx context.Context) (float64, error) {
sqOutput, err := client.GetServiceQuotaWithContext(ctx, &servicequotas.GetServiceQuotaInput{
QuotaCode: aws.String(quotaCode),
ServiceCode: aws.String(serviceCode),
})

if err != nil {
return 0, err
}

return *sqOutput.Quota.Value, nil
}
18 changes: 18 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,17 @@ type Route53Config struct {
Region string `yaml:"region"` // Use only a single Region for now, as the current metric is global
}

type EC2Config struct {
BaseConfig `yaml:"base,inline"`
Timeout time.Duration `yaml:"timeout"`
Regions []string `yaml:"regions"`
}

type Config struct {
RdsConfig RDSConfig `yaml:"rds"`
VpcConfig VPCConfig `yaml:"vpc"`
Route53Config Route53Config `yaml:"route53"`
EC2Config EC2Config `yaml:"ec2"`
}

func loadExporterConfiguration(logger log.Logger, configFile string) (*Config, error) {
Expand All @@ -88,6 +95,7 @@ func setupCollectors(logger log.Logger, configFile string, creds *credentials.Cr
}
level.Info(logger).Log("msg", "Configuring vpc with regions", "regions", strings.Join(config.VpcConfig.Regions, ","))
level.Info(logger).Log("msg", "Configuring rds with regions", "regions", strings.Join(config.RdsConfig.Regions, ","))
level.Info(logger).Log("msg", "Configuring ec2 with regions", "regions", strings.Join(config.EC2Config.Regions, ","))
level.Info(logger).Log("msg", "Configuring route53 with region", "region", config.Route53Config.Region)
var vpcSessions []*session.Session
level.Info(logger).Log("msg", "Will VPC metrics be gathered?", "vpc-enabled", config.VpcConfig.Enabled)
Expand All @@ -109,6 +117,16 @@ func setupCollectors(logger log.Logger, configFile string, creds *credentials.Cr
}
collectors = append(collectors, NewRDSExporter(rdsSessions, logger))
}
level.Info(logger).Log("msg", "Will EC2 metrics be gathered?", "ec2-enabled", config.EC2Config.Enabled)
var ec2Sessions []*session.Session
if config.EC2Config.Enabled {
for _, region := range config.EC2Config.Regions {
config := aws.NewConfig().WithCredentials(creds).WithRegion(region)
sess := session.Must(session.NewSession(config))
ec2Sessions = append(ec2Sessions, sess)
}
collectors = append(collectors, NewEC2Exporter(ec2Sessions, logger, config.EC2Config.Timeout))
}
level.Info(logger).Log("msg", "Will Route53 metrics be gathered?", "route53-enabled", config.Route53Config.Enabled)
if config.Route53Config.Enabled {
awsConfig := aws.NewConfig().WithCredentials(creds).WithRegion(config.Route53Config.Region)
Expand Down

0 comments on commit 7a3266d

Please sign in to comment.