-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug/rds calculation is missing additional costs #143
Changes from 5 commits
9b63f83
f18a930
51c4541
93178c9
22c3f3b
191af3a
814cec5
13ce6be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import ( | |
"finala/collector/aws/register" | ||
"finala/collector/config" | ||
"finala/expression" | ||
"fmt" | ||
"time" | ||
|
||
awsClient "github.com/aws/aws-sdk-go/aws" | ||
|
@@ -41,6 +42,14 @@ type DetectedAWSRDS struct { | |
collector.PriceDetectedFields | ||
} | ||
|
||
// RDSVolumeType will hold the available volume types for RDS types | ||
var rdsStorageType = map[string]string{ | ||
"gp2": "General Purpose", | ||
"standard": "Magnetic", | ||
"io1": "Provisioned IOPS", | ||
"aurora": "General Purpose-Aurora", | ||
} | ||
|
||
func init() { | ||
register.Registry("rds", NewRDSManager) | ||
} | ||
|
@@ -77,6 +86,16 @@ func (r *RDSManager) Detect(metrics []config.MetricConfig) (interface{}, error) | |
r.awsManager.GetCollector().CollectStart(r.Name) | ||
|
||
detected := []DetectedAWSRDS{} | ||
|
||
pricingRegionPrefix, err := r.awsManager.GetPricingClient().GetRegionPrefix(r.awsManager.GetRegion()) | ||
if err != nil { | ||
log.WithError(err).WithFields(log.Fields{ | ||
"region": r.awsManager.GetRegion(), | ||
}).Error("Could not get pricing region prefix") | ||
r.awsManager.GetCollector().CollectError(r.Name, err) | ||
return detected, err | ||
} | ||
|
||
instances, err := r.describeInstances(nil, nil) | ||
if err != nil { | ||
log.WithField("error", err).Error("could not describe rds instances") | ||
|
@@ -89,8 +108,6 @@ func (r *RDSManager) Detect(metrics []config.MetricConfig) (interface{}, error) | |
|
||
log.WithField("name", *instance.DBInstanceIdentifier).Debug("checking RDS") | ||
|
||
price, _ := r.awsManager.GetPricingClient().GetPrice(r.getPricingFilterInput(instance), "", r.awsManager.GetRegion()) | ||
|
||
for _, metric := range metrics { | ||
log.WithFields(log.Fields{ | ||
"name": *instance.DBInstanceIdentifier, | ||
|
@@ -136,9 +153,50 @@ func (r *RDSManager) Detect(metrics []config.MetricConfig) (interface{}, error) | |
"formula_value": formulaValue, | ||
"name": *instance.DBInstanceIdentifier, | ||
"instance_type": *instance.DBInstanceClass, | ||
"engine": *instance.Engine, | ||
"region": r.awsManager.GetRegion(), | ||
}).Info("RDS instance detected as unutilized resource") | ||
|
||
instancePricingFilters := r.getPricingInstanceFilterInput(instance) | ||
instancePrice, err := r.awsManager.GetPricingClient().GetPrice(instancePricingFilters, "", r.awsManager.GetRegion()) | ||
if err != nil { | ||
log.WithError(err).Error("Could not get rds instance price") | ||
continue | ||
} | ||
|
||
var hourlyStoragePrice float64 | ||
if rdsStorageType, found := rdsStorageType[*instance.StorageType]; found { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you say to move this logic into a function? |
||
var storagePricingFilters pricing.GetProductsInput | ||
switch *instance.Engine { | ||
case "aurora", "aurora-mysql", "aurora-postgresql": | ||
storagePricingFilters = r.getPricingAuroraStorageFilterInput(rdsStorageType, pricingRegionPrefix) | ||
default: | ||
deploymentOption := r.getPricingDeploymentOption(instance) | ||
storagePricingFilters = r.getPricingRDSStorageFilterInput(rdsStorageType, deploymentOption) | ||
} | ||
|
||
log.WithField("storage_filters", storagePricingFilters).Debug("pricing storage filters") | ||
storagePrice, err := r.awsManager.GetPricingClient().GetPrice(storagePricingFilters, "", r.awsManager.GetRegion()) | ||
if err != nil { | ||
log.WithError(err).Error("Could not get rds storage price") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you say to add the filter object into the log to improve the troubleshoot |
||
continue | ||
} | ||
|
||
hourlyStoragePrice = (storagePrice * float64(*instance.AllocatedStorage)) / collector.TotalMonthHours | ||
} else { | ||
log.WithField("rds_storage_type", *instance.StorageType).Warn("Could not find RDS storage type") | ||
continue | ||
} | ||
|
||
totalHourlyPrice := hourlyStoragePrice + instancePrice | ||
|
||
log.WithFields(log.Fields{ | ||
"instance_hour_price": instancePrice, | ||
"storage_hour_price": hourlyStoragePrice, | ||
"total_hour_price": totalHourlyPrice, | ||
"rds_AZ_multi": *instance.MultiAZ, | ||
"region": r.awsManager.GetRegion()}).Debug("Found the following price list") | ||
|
||
tags, err := r.client.ListTagsForResource(&rds.ListTagsForResourceInput{ | ||
ResourceName: instance.DBInstanceArn, | ||
}) | ||
|
@@ -159,8 +217,8 @@ func (r *RDSManager) Detect(metrics []config.MetricConfig) (interface{}, error) | |
PriceDetectedFields: collector.PriceDetectedFields{ | ||
ResourceID: *instance.DBInstanceArn, | ||
LaunchTime: *instance.InstanceCreateTime, | ||
PricePerHour: price, | ||
PricePerMonth: price * collector.TotalMonthHours, | ||
PricePerHour: totalHourlyPrice, | ||
PricePerMonth: totalHourlyPrice * collector.TotalMonthHours, | ||
Tag: tagsData, | ||
}, | ||
} | ||
|
@@ -183,14 +241,34 @@ func (r *RDSManager) Detect(metrics []config.MetricConfig) (interface{}, error) | |
} | ||
|
||
// getPricingFilterInput prepare document rds pricing filter | ||
func (r *RDSManager) getPricingFilterInput(instance *rds.DBInstance) pricing.GetProductsInput { | ||
func (r *RDSManager) getPricingInstanceFilterInput(instance *rds.DBInstance) pricing.GetProductsInput { | ||
|
||
deploymentOption := "Single-AZ" | ||
databaseEngine := r.getPricingDatabaseEngine(instance) | ||
deploymentOption := r.getPricingDeploymentOption(instance) | ||
|
||
if *instance.MultiAZ { | ||
deploymentOption = "Multi-AZ" | ||
return pricing.GetProductsInput{ | ||
ServiceCode: &r.servicePricingCode, | ||
Filters: []*pricing.Filter{ | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("databaseEngine"), | ||
Value: &databaseEngine, | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("instanceType"), | ||
Value: instance.DBInstanceClass, | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("deploymentOption"), | ||
Value: &deploymentOption, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (r *RDSManager) getPricingDatabaseEngine(instance *rds.DBInstance) string { | ||
var databaseEngine string | ||
switch *instance.Engine { | ||
case "postgres": | ||
|
@@ -202,28 +280,69 @@ func (r *RDSManager) getPricingFilterInput(instance *rds.DBInstance) pricing.Get | |
default: | ||
databaseEngine = *instance.Engine | ||
} | ||
return databaseEngine | ||
} | ||
|
||
func (r *RDSManager) getPricingDeploymentOption(instance *rds.DBInstance) string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a description on this function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm not seeing testing on this function. |
||
deploymentOption := "Single-AZ" | ||
|
||
if *instance.MultiAZ { | ||
deploymentOption = "Multi-AZ" | ||
} | ||
|
||
return deploymentOption | ||
} | ||
|
||
func (r *RDSManager) getPricingRDSStorageFilterInput(rdsStorageType string, deploymentOption string) pricing.GetProductsInput { | ||
cregev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return pricing.GetProductsInput{ | ||
ServiceCode: &r.servicePricingCode, | ||
Filters: []*pricing.Filter{ | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("databaseEngine"), | ||
Value: &databaseEngine, | ||
Field: awsClient.String("volumeType"), | ||
Value: awsClient.String(rdsStorageType), | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("instanceType"), | ||
Value: instance.DBInstanceClass, | ||
Field: awsClient.String("productFamily"), | ||
Value: awsClient.String("Database Storage"), | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("termType"), | ||
Value: awsClient.String("OnDemand"), | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("deploymentOption"), | ||
Value: &deploymentOption, | ||
Value: awsClient.String(deploymentOption), | ||
}, | ||
}, | ||
} | ||
} | ||
func (r *RDSManager) getPricingAuroraStorageFilterInput(rdsStorageType string, pricingRegionPrefix string) pricing.GetProductsInput { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a description on this function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm not seeing testing on this function. |
||
|
||
return pricing.GetProductsInput{ | ||
ServiceCode: &r.servicePricingCode, | ||
Filters: []*pricing.Filter{ | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("volumeType"), | ||
Value: awsClient.String(rdsStorageType), | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("databaseEngine"), | ||
Value: awsClient.String("Any"), | ||
}, | ||
{ | ||
Type: awsClient.String("TERM_MATCH"), | ||
Field: awsClient.String("usagetype"), | ||
Value: awsClient.String(fmt.Sprintf("%sAurora:StorageUsage", pricingRegionPrefix)), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
// describeInstances return list of rds instances | ||
|
@@ -244,7 +363,7 @@ func (r *RDSManager) describeInstances(Marker *string, instances []*rds.DBInstan | |
} | ||
|
||
for _, instance := range resp.DBInstances { | ||
// Ignore DocumentDB and Neptune engine types as we have a seperate | ||
// Ignore DocumentDB and Neptune engine types as we have a separate | ||
// module for them and the default API call returns them | ||
if *instance.Engine != "docdb" && *instance.Engine != "neptune" { | ||
instances = append(instances, instance) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The price calculation really needs to be in the metrics loop?
the price can be changed when we have a different metric?