Skip to content

Commit

Permalink
Merge pull request #649 from tailwarden/feature/tech-989
Browse files Browse the repository at this point in the history
added missing RDS cost estimate
  • Loading branch information
mlabouardy authored Mar 23, 2023
2 parents 19f5acb + 4246048 commit eb2bc6d
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 71 deletions.
12 changes: 12 additions & 0 deletions models/price.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package models

type PricingResult struct {
Product struct {
Sku string `json:"sku"`
} `json:"product"`
Terms map[string]map[string]map[string]map[string]struct {
PricePerUnit struct {
USD string `json:"USD"`
} `json:"pricePerUnit"`
} `json:"terms"`
}
73 changes: 10 additions & 63 deletions providers/aws/ec2/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,67 +14,14 @@ import (
"github.com/aws/aws-sdk-go-v2/service/pricing"
"github.com/aws/aws-sdk-go-v2/service/pricing/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
"github.com/tailwarden/komiser/utils"
)

type Ec2Product struct {
Sku string `json:"sku"`
ProductFamily string `json:"productFamily"`
Attributes struct {
Location string `json:"location"`
InstanceType string `json:"instanceType"`
Tenancy string `json:"tenancy"`
OperatingSystem string `json:"operatingSystem"`
LicenseModel string `json:"licenseModel"`
UsageType string `json:"usagetype"`
PreInstalledSw string `json:"preInstalledSw"`
}
}

type PricingResult struct {
Product Ec2Product `json:"product"`
Terms map[string]map[string]map[string]map[string]struct {
PricePerUnit struct {
USD string `json:"USD"`
} `json:"pricePerUnit"`
} `json:"terms"`
}

func GetRegionName(code string) string {
regions := map[string]string{
"us-east-2": "US East (Ohio)",
"us-east-1": "US East (N. Virginia)",
"us-west-1": "US West (N. California)",
"us-west-2": "US West (Oregon)",
"af-south-1": "Africa (Cape Town)",
"ap-east-1": "Asia Pacific (Hong Kong)",
"ap-south-2": "Asia Pacific (Hyderabad)",
"ap-southeast-3": "Asia Pacific (Jakarta)",
"ap-south-1": "Asia Pacific (Mumbai)",
"ap-northeast-3": "Asia Pacific (Osaka)",
"ap-northeast-2": "Asia Pacific (Seoul)",
"ap-southeast-1": "Asia Pacific (Singapore)",
"ap-southeast-2": "Asia Pacific (Sydney)",
"ap-northeast-1": "Asia Pacific (Tokyo)",
"ca-central-1": "Canada (Central)",
"eu-central-1": "EU (Frankfurt)",
"eu-west-1": "EU (Ireland)",
"eu-west-2": "EU (London)",
"eu-south-1": "EU (Milan)",
"eu-west-3": "EU (Paris)",
"eu-south-2": "EU (Spain)",
"eu-north-1": "EU (Stockholm)",
"eu-central-2": "EU (Zurich)",
"sa-east-1": "South America (São Paulo)",
}
return regions[code]
}

func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {
func Instances(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
var nextToken string
resources := make([]Resource, 0)
resources := make([]models.Resource, 0)
ec2Client := ec2.NewFromConfig(*client.AWSClient)

stsClient := sts.NewFromConfig(*client.AWSClient)
Expand All @@ -100,14 +47,14 @@ func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {

for _, reservations := range output.Reservations {
for _, instance := range reservations.Instances {
tags := make([]Tag, 0)
tags := make([]models.Tag, 0)

name := ""
for _, tag := range instance.Tags {
if *tag.Key == "Name" {
name = *tag.Value
}
tags = append(tags, Tag{
tags = append(tags, models.Tag{
Key: *tag.Key,
Value: *tag.Value,
})
Expand Down Expand Up @@ -135,8 +82,8 @@ func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {
Type: types.FilterTypeTermMatch,
},
{
Field: aws.String("location"),
Value: aws.String(GetRegionName(client.AWSClient.Region)),
Field: aws.String("regionCode"),
Value: aws.String(client.AWSClient.Region),
Type: types.FilterTypeTermMatch,
},
{
Expand All @@ -157,7 +104,7 @@ func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {
b, _ := json.Marshal(pricingOutput.PriceList[0])
s, _ := strconv.Unquote(string(b))

pricingResult := PricingResult{}
pricingResult := models.PricingResult{}
err = json.Unmarshal([]byte(s), &pricingResult)
if err != nil {
log.WithError(err).Error("could not unmarshal")
Expand All @@ -171,7 +118,7 @@ func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {

resourceArn := fmt.Sprintf("arn:aws:ec2:%s:%s:instance/%s", client.AWSClient.Region, *accountId, *instance.InstanceId)

resources = append(resources, Resource{
resources = append(resources, models.Resource{
Provider: "AWS",
Account: client.Name,
Service: "EC2",
Expand Down
76 changes: 68 additions & 8 deletions providers/aws/rds/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,42 @@ package rds

import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"

log "github.com/sirupsen/logrus"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/pricing"
"github.com/aws/aws-sdk-go-v2/service/pricing/types"
"github.com/aws/aws-sdk-go-v2/service/rds"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
"github.com/tailwarden/komiser/utils"
)

func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {
func Instances(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
var config rds.DescribeDBInstancesInput
resources := make([]Resource, 0)
resources := make([]models.Resource, 0)
rdsClient := rds.NewFromConfig(*client.AWSClient)

oldRegion := client.AWSClient.Region
client.AWSClient.Region = "us-east-1"
pricingClient := pricing.NewFromConfig(*client.AWSClient)
client.AWSClient.Region = oldRegion

for {
output, err := rdsClient.DescribeDBInstances(ctx, &config)
if err != nil {
return resources, err
}

for _, instance := range output.DBInstances {
tags := make([]Tag, 0)
tags := make([]models.Tag, 0)
for _, tag := range instance.TagList {
tags = append(tags, Tag{
tags = append(tags, models.Tag{
Key: *tag.Key,
Value: *tag.Value,
})
Expand All @@ -40,13 +50,63 @@ func Instances(ctx context.Context, client ProviderClient) ([]Resource, error) {
_instanceName = *instance.DBName
}

resources = append(resources, Resource{
startOfMonth := utils.BeginningOfMonth(time.Now())
hourlyUsage := 0
if (*instance.InstanceCreateTime).Before(startOfMonth) {
hourlyUsage = int(time.Since(startOfMonth).Hours())
} else {
hourlyUsage = int(time.Since(*instance.InstanceCreateTime).Hours())
}

pricingOutput, err := pricingClient.GetProducts(ctx, &pricing.GetProductsInput{
ServiceCode: aws.String("AmazonRDS"),
Filters: []types.Filter{
{
Field: aws.String("instanceType"),
Value: aws.String(*instance.DBInstanceClass),
Type: types.FilterTypeTermMatch,
},
{
Field: aws.String("regionCode"),
Value: aws.String(client.AWSClient.Region),
Type: types.FilterTypeTermMatch,
},
{
Field: aws.String("databaseEngine"),
Value: aws.String(*instance.Engine),
Type: types.FilterTypeTermMatch,
},
},
MaxResults: aws.Int32(1),
})
if err != nil {
log.Warnf("Couldn't fetch invocations metric for %s", _instanceName)
}

hourlyCost := 0.0
if pricingOutput != nil && len(pricingOutput.PriceList) > 0 {
b, _ := json.Marshal(pricingOutput.PriceList[0])
s, _ := strconv.Unquote(string(b))

pricingResult := models.PricingResult{}
err = json.Unmarshal([]byte(s), &pricingResult)
if err != nil {
log.WithError(err).Error("could not unmarshal")
}

hourlyCostRaw := pricingResult.Terms["OnDemand"][fmt.Sprintf("%s.JRTCKXETXF", pricingResult.Product.Sku)]["priceDimensions"][fmt.Sprintf("%s.JRTCKXETXF.6YS6EN2CT7", pricingResult.Product.Sku)].PricePerUnit.USD
hourlyCost, _ = strconv.ParseFloat(hourlyCostRaw, 64)
}

monthlyCost := float64(hourlyUsage) * hourlyCost

resources = append(resources, models.Resource{
Provider: "AWS",
Account: client.Name,
Service: "RDS Instance",
Region: client.AWSClient.Region,
ResourceId: *instance.DBInstanceArn,
Cost: 0,
Cost: monthlyCost,
Name: _instanceName,
FetchedAt: time.Now(),
Tags: tags,
Expand Down

0 comments on commit eb2bc6d

Please sign in to comment.