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
GTM-52 Fix parsing of GCP pricing and create new node diagnostic pricing API #2083
Conversation
02f7a10
to
214ebb0
Compare
214ebb0
to
933b097
Compare
|
||
if (instanceType == "ram" || instanceType == "cpu") && strings.Contains(strings.ToUpper(product.Description), "T2D AMD") { | ||
instanceType = "t2dstandard" | ||
} | ||
if (instanceType == "ram" || instanceType == "cpu") && strings.Contains(strings.ToUpper(product.Description), "T2A ARM") { | ||
instanceType = "t2astandard" | ||
} | ||
|
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.
This fixes the proximate user issue.
if len(product.PricingInfo) > 0 { | ||
lastRateIndex := len(product.PricingInfo[0].PricingExpression.TieredRates) - 1 | ||
if lastRateIndex >= 0 { | ||
nanos = product.PricingInfo[0].PricingExpression.TieredRates[lastRateIndex].UnitPrice.Nanos | ||
unitsBaseCurrency, err = strconv.Atoi(product.PricingInfo[0].PricingExpression.TieredRates[lastRateIndex].UnitPrice.Units) | ||
if err != nil { | ||
return nil, "", fmt.Errorf("error parsing base unit price for instance: %w", err) | ||
} | ||
} else { | ||
continue |
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.
This fixes some panics. (We used to check the length of the slice after we'd already accessed [0]
haha.)
} | ||
break |
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.
Removing this, and the other break, allows pricing to work for multiple instance types from the same family.
} | ||
break |
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.
Removing this, and the other break, allows pricing to work for multiple instance types from the same family.
product = &GCPPricing{} | ||
product.Node = &models.Node{ | ||
log.Debugf("GCP Billing API: key '%s': RAM price: %f", candidateKey, hourlyPrice) | ||
pricing := &GCPPricing{} |
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.
Setting this to a new variable (i.e. pricing :=
not product =
) prevents nil
issues and other failures when the original product data needs to be reused later on.
product = &GCPPricing{} | ||
product.Node = &models.Node{ | ||
log.Debugf("GCP Billing API: key '%s': RAM price: %f", candidateKeyGPU, hourlyPrice) | ||
pricing := &GCPPricing{} |
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.
Setting this to a new variable (i.e. pricing :=
not product =
) prevents nil
issues and other failures when the original product data needs to be reused later on.
product = &GCPPricing{} | ||
product.Node = &models.Node{ | ||
log.Debugf("GCP Billing API: key '%s': CPU price: %f", candidateKey, hourlyPrice) | ||
pricing := &GCPPricing{} |
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.
Setting this to a new variable (i.e. pricing :=
not product =
) prevents nil
issues and other failures when the original product data needs to be reused later on.
product = &GCPPricing{} | ||
product.Node = &models.Node{ | ||
log.Debugf("GCP Billing API: key '%s': CPU price: %f", candidateKeyGPU, hourlyPrice) | ||
pricing := &GCPPricing{} |
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.
Setting this to a new variable (i.e. pricing :=
not product =
) prevents nil
issues and other failures when the original product data needs to be reused later on.
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.
brutal. so this is your mutated loop var issue
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.
Yep. Seems obvious now, but was not fun to find.
933b097
to
efa7cae
Compare
} | ||
if _, ok := gcpPricingList[candidateKey]; ok { | ||
log.Debugf("GCP Billing API: key '%s': RAM price: %f", candidateKey, hourlyPrice) |
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.
Unified these logs so that they're more legible and clearly related to each other:
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,a2highgpu,ondemand': RAM price: 0.004237"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,a2highgpu,ondemand,gpu': RAM price: 0.004237"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,a2highgpu,ondemand': CPU price: 0.031611"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,a2highgpu,ondemand,gpu': CPU price: 0.031611"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'asia-southeast1,t2dstandard,ondemand': RAM price: 68.205000"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'asia-southeast1,t2dstandard,ondemand,gpu': RAM price: 68.205000"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'asia-southeast1,t2dstandard,ondemand': CPU price: 508.934997"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'asia-southeast1,t2dstandard,ondemand,gpu': CPU price: 508.934997"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2medium,ondemand': CPU price: 327.173848"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2medium,ondemand,gpu': CPU price: 327.173848"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2standard,ondemand': CPU price: 327.173848"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2standard,ondemand,gpu': CPU price: 327.173848"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2medium,ondemand': RAM price: 43.852950"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2medium,ondemand,gpu': RAM price: 43.852950"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2standard,ondemand': RAM price: 43.852950"}
{"level":"debug","time":"2023-08-11T16:46:04-06:00","message":"GCP Billing API: key 'us-central1,e2standard,ondemand,gpu': RAM price: 43.852950"}
@@ -1539,25 +1552,55 @@ func (gcp *GCP) isValidPricingKey(key models.Key) bool { | |||
} | |||
|
|||
// NodePricing returns GCP pricing data for a single node | |||
func (gcp *GCP) NodePricing(key models.Key) (*models.Node, error) { | |||
func (gcp *GCP) NodePricing(key models.Key) (*models.Node, models.PricingMetadata, error) { | |||
meta := models.PricingMetadata{} |
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.
This function is not materially changed. It just has extra lines adding in the metadata for the new diagnostics API.
efa7cae
to
d00b7dc
Compare
// Load a reader object on a portion of a GCP api response | ||
// Confirm that the resting *GCP object contains the correctly parsed pricing info | ||
func TestParsePage(t *testing.T) { | ||
func TestKeyFeatures(t *testing.T) { |
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.
This is a new test.
// tests basic parsing of GCP pricing API responses | ||
// Load a reader object on a portion of a GCP api response | ||
// Confirm that the resting *GCP object contains the correctly parsed pricing info | ||
func TestParsePage(t *testing.T) { |
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.
This test has been updated to add more SKUs, and the SKUs themselves were moved to a testing file.
@@ -273,7 +273,7 @@ type Provider interface { | |||
GetAddresses() ([]byte, error) | |||
GetDisks() ([]byte, error) | |||
GetOrphanedResources() ([]OrphanedResource, error) | |||
NodePricing(Key) (*Node, error) | |||
NodePricing(Key) (*Node, PricingMetadata, error) |
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.
This change just allows us to use the very same function for getting pricing, and reporting metadata about where that data came from.
product = &GCPPricing{} | ||
product.Node = &models.Node{ | ||
log.Debugf("GCP Billing API: key '%s': CPU price: %f", candidateKeyGPU, hourlyPrice) | ||
pricing := &GCPPricing{} |
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.
brutal. so this is your mutated loop var issue
…ing API Signed-off-by: Niko Kovacevic <nikovacevic@gmail.com>
d00b7dc
to
42976a2
Compare
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.
Trusting Alex's review
What does this PR change?
e2-small
,e2-standard
) present.Does this PR relate to any other PRs?
How will this PR impact users?
Does this PR address any GitHub or Zendesk issues?
How was this PR tested?
Here's evidence of the node type at issue, working for currency IDR:
Does this PR require changes to documentation?
Have you labeled this PR and its corresponding Issue as "next release" if it should be part of the next OpenCost release? If not, why not?