Skip to content

Commit

Permalink
List EBS Volumes
Browse files Browse the repository at this point in the history
  • Loading branch information
paulajulve committed Jul 12, 2024
1 parent 240ed05 commit 5f47120
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 7 deletions.
30 changes: 30 additions & 0 deletions pkg/aws/ec2/compute.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,33 @@ func ClusterNameFromInstance(instance types.Instance) string {
}
return ""
}

func ListEBSVolumes(ctx context.Context, client *ec2.EC2) ([]types.Volume, error) {
// Paula: do we want to exclude the snapshots? hmm...
params := &ec22.DescribeVolumesInput{
Filters: []types.Filter{
// exclude snapshots
{
Name: aws.String("snapshot-id"),
Values: []string{""},
},
},
}

pager := ec22.NewDescribeVolumesPaginator(*client, params)
var volumes []types.Volume

for pager.HasMorePages() {
resp, err := pager.NextPage(ctx)
if err != nil {
return nil, err
}

volumes = append(volumes, resp.Volumes...)
if resp.NextToken == nil || *resp.NextToken == "" {
break
}
}

return volumes, nil
}
67 changes: 60 additions & 7 deletions pkg/aws/ec2/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ var (
[]string{"instance", "region", "family", "machine_type", "cluster_name", "price_tier"},
nil,
)
persistentVolumeHourlyCostDesc = prometheus.NewDesc(
prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "persistent_volume_usd_per_hour"),
"The cost of an AWS EBS Volume in USD.",
// []string{"cluster_name", "namespace", "persistentvolume", "region", "project", "storage_class", "disk_type"},
[]string{"persistentvolume", "az", "storage_class"},
nil,
)
)

// Collector is a prometheus collector that collects metrics from AWS EKS clusters.
Expand Down Expand Up @@ -169,15 +176,20 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
c.StorageScrapingInterval = time.Now().Add(c.ScrapeInterval)
}

wg := sync.WaitGroup{}
wg.Add(len(c.Regions))
wgInstances := sync.WaitGroup{}
wgInstances.Add(len(c.Regions))
instanceCh := make(chan []ec2Types.Reservation, len(c.Regions))

wgVolumes := sync.WaitGroup{}
wgVolumes.Add(len(c.Regions))
volumeCh := make(chan []ec2Types.Volume, len(c.Regions))

for _, region := range c.Regions {
go func(region ec2Types.Region) {
ctx := context.Background()
now := time.Now()
c.logger.LogAttrs(ctx, slog.LevelInfo, "Fetching instances", slog.String("region", *region.RegionName))
defer wg.Done()
defer wgInstances.Done()
client := c.ec2RegionClients[*region.RegionName]
reservations, err := ListComputeInstances(context.Background(), client)
if err != nil {
Expand All @@ -193,17 +205,44 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
)
instanceCh <- reservations
}(region)

go func(region ec2Types.Region) {
ctx := context.Background()
now := time.Now()
c.logger.LogAttrs(ctx, slog.LevelInfo, "Fetching volumes", slog.String("region", *region.RegionName))
defer wgVolumes.Done()
client := c.ec2RegionClients[*region.RegionName]

volumes, err := ListEBSVolumes(context.Background(), &client)
if err != nil {
c.logger.LogAttrs(ctx, slog.LevelError, "Could not list EBS volumes",
slog.String("region", *region.RegionName),
slog.String("message", err.Error()))
return
}
c.logger.LogAttrs(ctx, slog.LevelInfo, "Successfully listed volumes",
slog.String("region", *region.RegionName),
slog.Int("volumes", len(volumes)),
slog.Duration("duration", time.Since(now)),
)
volumeCh <- volumes
}(region)
}
go func() {
wg.Wait()
wgInstances.Wait()
close(instanceCh)
}()
c.emitMetricsFromChannel(instanceCh, ch)
go func() {
wgVolumes.Wait()
close(volumeCh)
}()
c.emitMetricsFromReservationsChannel(instanceCh, ch)
c.emitMetricsFromVolumesChannel(volumeCh, ch)
c.logger.LogAttrs(context.TODO(), slog.LevelInfo, "Finished collect", slog.Duration("duration", time.Since(start)))
return nil
}

func (c *Collector) emitMetricsFromChannel(reservationsCh chan []ec2Types.Reservation, ch chan<- prometheus.Metric) {
func (c *Collector) emitMetricsFromReservationsChannel(reservationsCh chan []ec2Types.Reservation, ch chan<- prometheus.Metric) {
for reservations := range reservationsCh {
for _, reservation := range reservations {
for _, instance := range reservation.Instances {
Expand Down Expand Up @@ -246,8 +285,22 @@ func (c *Collector) emitMetricsFromChannel(reservationsCh chan []ec2Types.Reserv
}
}

func (c *Collector) emitMetricsFromVolumesChannel(volumesCh chan []ec2Types.Volume, ch chan<- prometheus.Metric) {
for volumes := range volumesCh {
for _, volume := range volumes {
labelValues := []string{
*volume.VolumeId,
*volume.AvailabilityZone,
string(volume.VolumeType),
}

ch <- prometheus.MustNewConstMetric(persistentVolumeHourlyCostDesc, prometheus.GaugeValue, 42, labelValues...)
}
}
}

func (c *Collector) CheckReadiness() bool {
return c.pricingMap.CheckReadiness()
return c.computePricingMap.CheckReadiness()
}

func (c *Collector) Describe(ch chan<- *prometheus.Desc) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/aws/services/ec2/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ type EC2 interface {
DescribeInstances(ctx context.Context, e *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
DescribeRegions(ctx context.Context, e *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error)
DescribeSpotPriceHistory(ctx context.Context, input *ec2.DescribeSpotPriceHistoryInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSpotPriceHistoryOutput, error)
DescribeVolumes(context.Context, *ec2.DescribeVolumesInput, ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error)
}

0 comments on commit 5f47120

Please sign in to comment.