diff --git a/CHANGELOG.md b/CHANGELOG.md index cc738aef..5983ff54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Add the option to filter resources by tags in the API [[PR-73](https://github.com/similarweb/finala/pull/73)] -- Ignore Neptune based instances for RDS instances collection -- Implement golangci-lint and golang fmt +- Fix the pricing for ELB/ELBV2 [[PR-76](https://github.com/similarweb/finala/pull/76)] +- Ignore Neptune based instances for RDS instances collection [[PR-71](https://github.com/similarweb/finala/pull/71)] +- Implement golangci-lint and golang fmt [[PR-78](https://github.com/similarweb/finala/pull/78)] ## [0.3.3] ### Added diff --git a/collector/aws/elb.go b/collector/aws/elb.go index a576fe24..dd64f18b 100644 --- a/collector/aws/elb.go +++ b/collector/aws/elb.go @@ -51,7 +51,7 @@ func NewELBManager(collector collector.CollectorDescriber, client ELBClientDescr pricingClient: pricing, region: region, namespace: "AWS/ELB", - servicePricingCode: "AmazonEC2", + servicePricingCode: "AWSELB", Name: fmt.Sprintf("%s_elb", ResourcePrefix), } } @@ -73,16 +73,18 @@ func (el *ELBManager) Detect() ([]DetectedELB, error) { detectedELB := []DetectedELB{} - instances, err := el.DescribeLoadbalancers(nil, nil) + pricingRegionPrefix, err := el.pricingClient.GetRegionPrefix(el.region) if err != nil { + log.WithError(err).WithFields(log.Fields{ + "region": el.region, + }).Error("Could not get pricing region prefix") + el.updateErrorServiceStatus(err) + return detectedELB, err + } - el.collector.UpdateServiceStatus(collector.EventCollector{ - ResourceName: el.Name, - Data: collector.EventStatusData{ - Status: collector.EventError, - ErrorMessage: err.Error(), - }, - }) + instances, err := el.DescribeLoadbalancers(nil, nil) + if err != nil { + el.updateErrorServiceStatus(err) return detectedELB, err } @@ -90,8 +92,13 @@ func (el *ELBManager) Detect() ([]DetectedELB, error) { for _, instance := range instances { log.WithField("name", *instance.LoadBalancerName).Debug("checking elb") - - price, _ := el.pricingClient.GetPrice(el.GetPricingFilterInput(), "", el.region) + price, _ := el.pricingClient.GetPrice(el.GetPricingFilterInput([]*pricing.Filter{ + { + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("usagetype"), + Value: awsClient.String(fmt.Sprintf("%sLoadBalancerUsage", pricingRegionPrefix)), + }, + }), "", el.region) for _, metric := range el.metrics { @@ -196,33 +203,27 @@ func (el *ELBManager) Detect() ([]DetectedELB, error) { } // GetPricingFilterInput prepare document elb pricing filter -func (el *ELBManager) GetPricingFilterInput() *pricing.GetProductsInput { +func (el *ELBManager) GetPricingFilterInput(extraFilters []*pricing.Filter) *pricing.GetProductsInput { + filters := []*pricing.Filter{ + { + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("termType"), + Value: awsClient.String("OnDemand"), + }, + { + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("productFamily"), + Value: awsClient.String("Load Balancer"), + }, + } + + if extraFilters != nil { + filters = append(filters, extraFilters...) + } return &pricing.GetProductsInput{ ServiceCode: &el.servicePricingCode, - Filters: []*pricing.Filter{ - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("usagetype"), - Value: awsClient.String("LoadBalancerUsage"), - }, - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("productFamily"), - Value: awsClient.String("Load Balancer-Application"), - }, - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("TermType"), - Value: awsClient.String("OnDemand"), - }, - - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("group"), - Value: awsClient.String("ELB:Balancer"), - }, - }, + Filters: filters, } } @@ -251,3 +252,14 @@ func (el *ELBManager) DescribeLoadbalancers(marker *string, loadbalancers []*elb return loadbalancers, nil } + +// updateErrorServiceStatus reports when elb can't collect data +func (el *ELBManager) updateErrorServiceStatus(err error) { + el.collector.UpdateServiceStatus(collector.EventCollector{ + ResourceName: el.Name, + Data: collector.EventStatusData{ + Status: collector.EventError, + ErrorMessage: err.Error(), + }, + }) +} diff --git a/collector/aws/elb_test.go b/collector/aws/elb_test.go index c004229d..cc4ec631 100644 --- a/collector/aws/elb_test.go +++ b/collector/aws/elb_test.go @@ -107,32 +107,41 @@ func TestDetectELB(t *testing.T) { } func TestDetectELBError(t *testing.T) { - - collector := testutils.NewMockCollector() - mockCloudwatchClient := MockAWSCloudwatchClient{ - responseMetricStatistics: defaultResponseMetricStatistics, - } - cloutwatchManager := aws.NewCloudWatchManager(&mockCloudwatchClient) - pricingManager := aws.NewPricingManager(&defaultPricingMock, "us-east-1") - mockClient := MockAWSELBClient{ err: errors.New(""), } - elbManager := aws.NewELBManager(collector, &mockClient, cloutwatchManager, pricingManager, defaultMetricConfig, "us-east-1") - - response, _ := elbManager.Detect() - - if len(response) != 0 { - t.Fatalf("unexpected elb detected, got %d expected %d", len(response), 0) + testCases := []struct { + region string + expectedPrefix string + expectedError error + }{ + {"us-east-1", "", mockClient.err}, + {"no-region-1", "", aws.ErrRegionNotFound}, } + for _, tc := range testCases { + collector := testutils.NewMockCollector() + mockCloudwatchClient := MockAWSCloudwatchClient{ + responseMetricStatistics: defaultResponseMetricStatistics, + } + cloutwatchManager := aws.NewCloudWatchManager(&mockCloudwatchClient) + pricingManager := aws.NewPricingManager(&defaultPricingMock, "us-east-1") + elbManager := aws.NewELBManager(collector, &mockClient, cloutwatchManager, pricingManager, defaultMetricConfig, tc.region) + response, err := elbManager.Detect() - if len(collector.Events) != 0 { - t.Fatalf("unexpected collector elb resources, got %d expected %d", len(collector.Events), 0) - } + if len(response) != 0 { + t.Fatalf("unexpected elb detected, got %d expected %d", len(response), 0) + } - if len(collector.EventsCollectionStatus) != 2 { - t.Fatalf("unexpected resource status events count, got %d expected %d", len(collector.EventsCollectionStatus), 2) - } + if len(collector.Events) != 0 { + t.Fatalf("unexpected collector elb resources, got %d expected %d", len(collector.Events), 0) + } + if len(collector.EventsCollectionStatus) != 2 { + t.Fatalf("unexpected resource status events count, got %d expected %d", len(collector.EventsCollectionStatus), 2) + } + if !errors.Is(err, tc.expectedError) { + t.Fatalf("unexpected error response, got: %v, expected: %v", err, tc.expectedError) + } + } } diff --git a/collector/aws/elbv2.go b/collector/aws/elbv2.go index c58dfd96..bf83ff39 100644 --- a/collector/aws/elbv2.go +++ b/collector/aws/elbv2.go @@ -29,18 +29,49 @@ type ELBV2Manager struct { pricingClient *PricingManager metrics []config.MetricConfig region string - namespace string servicePricingCode string Name string } -// DetectedELBV2 define the detected AWS ELB instances +// DetectedELBV2 defines the detected AWS ELB instances type DetectedELBV2 struct { Metric string Region string + Type string collector.PriceDetectedFields } +// loadBalancerConfig defines loadbalancer's configuration of metrics and pricing +type loadBalancerConfig struct { + cloudWatchNamespace string + pricingfilters []*pricing.Filter +} + +// loadBalancersConfig defines loadbalancers configuration of metrics and pricing for +// Multiple types of LoadBalancers. +var loadBalancersConfig = map[string]loadBalancerConfig{ + "application": { + cloudWatchNamespace: "AWS/ApplicationELB", + pricingfilters: []*pricing.Filter{ + { + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("productFamily"), + Value: awsClient.String("Load Balancer-Application"), + }, + }, + }, + "network": { + cloudWatchNamespace: "AWS/NetworkELB", + pricingfilters: []*pricing.Filter{ + { + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("productFamily"), + Value: awsClient.String("Load Balancer-Network"), + }, + }, + }, +} + // NewELBV2Manager implements AWS GO SDK func NewELBV2Manager(collector collector.CollectorDescriber, client ELBV2ClientDescreptor, cloudWatchCLient *CloudwatchManager, pricing *PricingManager, metrics []config.MetricConfig, region string) *ELBV2Manager { @@ -51,8 +82,7 @@ func NewELBV2Manager(collector collector.CollectorDescriber, client ELBV2ClientD metrics: metrics, pricingClient: pricing, region: region, - namespace: "AWS/ApplicationELB", - servicePricingCode: "AmazonEC2", + servicePricingCode: "AWSELB", Name: fmt.Sprintf("%s_elbv2", ResourcePrefix), } } @@ -74,26 +104,39 @@ func (el *ELBV2Manager) Detect() ([]DetectedELBV2, error) { detectedELBV2 := []DetectedELBV2{} + pricingRegionPrefix, err := el.pricingClient.GetRegionPrefix(el.region) + if err != nil { + log.WithError(err).WithFields(log.Fields{ + "region": el.region, + }).Error("Could not get pricing region prefix") + el.updateErrorServiceStatus(err) + return detectedELBV2, err + } + instances, err := el.DescribeLoadbalancers(nil, nil) if err != nil { - el.collector.UpdateServiceStatus(collector.EventCollector{ - ResourceName: el.Name, - Data: collector.EventStatusData{ - Status: collector.EventError, - ErrorMessage: err.Error(), - }, - }) + el.updateErrorServiceStatus(err) return detectedELBV2, err } now := time.Now() for _, instance := range instances { - - log.WithField("name", *instance.LoadBalancerName).Debug("cheking elbV2") - - price, _ := el.pricingClient.GetPrice(el.GetPricingFilterInput(), "", el.region) - + var cloudWatchNameSpace string + var price float64 + if loadBalancerConfig, found := loadBalancersConfig[*instance.Type]; found { + cloudWatchNameSpace = loadBalancerConfig.cloudWatchNamespace + + log.WithField("name", *instance.LoadBalancerName).Debug("checking elbV2") + + loadBalancerConfig.pricingfilters = append( + loadBalancerConfig.pricingfilters, &pricing.Filter{ + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("usagetype"), + Value: awsClient.String(fmt.Sprintf("%sLoadBalancerUsage", pricingRegionPrefix)), + }) + price, _ = el.pricingClient.GetPrice(el.GetPricingFilterInput(loadBalancerConfig.pricingfilters), "", el.region) + } for _, metric := range el.metrics { log.WithFields(log.Fields{ @@ -110,7 +153,7 @@ func (el *ELBV2Manager) Detect() ([]DetectedELBV2, error) { elbv2Name := regx.ReplaceAllString(*instance.LoadBalancerArn, "") metricInput := cloudwatch.GetMetricStatisticsInput{ - Namespace: &el.namespace, + Namespace: &cloudWatchNameSpace, MetricName: &metric.Description, Period: &period, StartTime: &metricEndTime, @@ -169,6 +212,7 @@ func (el *ELBV2Manager) Detect() ([]DetectedELBV2, error) { elbv2 := DetectedELBV2{ Region: el.region, Metric: metric.Description, + Type: *instance.Type, PriceDetectedFields: collector.PriceDetectedFields{ ResourceID: *instance.LoadBalancerName, LaunchTime: *instance.CreatedTime, @@ -203,33 +247,22 @@ func (el *ELBV2Manager) Detect() ([]DetectedELBV2, error) { } // GetPricingFilterInput prepare document elb pricing filter -func (el *ELBV2Manager) GetPricingFilterInput() *pricing.GetProductsInput { +func (el *ELBV2Manager) GetPricingFilterInput(extraFilters []*pricing.Filter) *pricing.GetProductsInput { + filters := []*pricing.Filter{ + { + Type: awsClient.String("TERM_MATCH"), + Field: awsClient.String("termType"), + Value: awsClient.String("OnDemand"), + }, + } + + if extraFilters != nil { + filters = append(filters, extraFilters...) + } return &pricing.GetProductsInput{ ServiceCode: &el.servicePricingCode, - Filters: []*pricing.Filter{ - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("usagetype"), - Value: awsClient.String("LoadBalancerUsage"), - }, - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("productFamily"), - Value: awsClient.String("Load Balancer-Application"), - }, - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("TermType"), - Value: awsClient.String("OnDemand"), - }, - - { - Type: awsClient.String("TERM_MATCH"), - Field: awsClient.String("group"), - Value: awsClient.String("ELB:Balancer"), - }, - }, + Filters: filters, } } @@ -258,3 +291,14 @@ func (el *ELBV2Manager) DescribeLoadbalancers(marker *string, loadbalancers []*e return loadbalancers, nil } + +// updateErrorServiceStatus reports when elbv2 can't collect data +func (el *ELBV2Manager) updateErrorServiceStatus(err error) { + el.collector.UpdateServiceStatus(collector.EventCollector{ + ResourceName: el.Name, + Data: collector.EventStatusData{ + Status: collector.EventError, + ErrorMessage: err.Error(), + }, + }) +} diff --git a/collector/aws/elbv2_test.go b/collector/aws/elbv2_test.go index e2d3a8e0..6d40ba9d 100644 --- a/collector/aws/elbv2_test.go +++ b/collector/aws/elbv2_test.go @@ -15,6 +15,7 @@ import ( var defaultELBV2Mock = elbv2.DescribeLoadBalancersOutput{ LoadBalancers: []*elbv2.LoadBalancer{ { + Type: awsClient.String("application"), LoadBalancerName: awsClient.String("i-1"), LoadBalancerArn: awsClient.String("i-1"), CreatedTime: testutils.TimePointer(time.Now()), @@ -105,36 +106,46 @@ func TestDetectELBV2(t *testing.T) { if len(collector.EventsCollectionStatus) != 2 { t.Fatalf("unexpected resource status events count, got %d expected %d", len(collector.EventsCollectionStatus), 2) } - } func TestDetectELBV2Error(t *testing.T) { - - collector := testutils.NewMockCollector() - mockCloudwatchClient := MockAWSCloudwatchClient{ - responseMetricStatistics: defaultResponseMetricStatistics, - } - cloutwatchManager := aws.NewCloudWatchManager(&mockCloudwatchClient) - pricingManager := aws.NewPricingManager(&defaultPricingMock, "us-east-1") - mockClient := MockAWSELBV2Client{ err: errors.New(""), } - - elbv2Manager := aws.NewELBV2Manager(collector, &mockClient, cloutwatchManager, pricingManager, defaultMetricConfig, "us-east-1") - - response, _ := elbv2Manager.Detect() - - if len(response) != 0 { - t.Fatalf("unexpected elbv2 detected, got %d expected %d", len(response), 0) - } - - if len(collector.Events) != 0 { - t.Fatalf("unexpected collector elbv2 resources, got %d expected %d", len(collector.Events), 0) + testCases := []struct { + region string + expectedPrefix string + expectedError error + }{ + {"us-east-1", "", mockClient.err}, + {"no-region-1", "", aws.ErrRegionNotFound}, } - if len(collector.EventsCollectionStatus) != 2 { - t.Fatalf("unexpected resource status events count, got %d expected %d", len(collector.EventsCollectionStatus), 2) + for _, tc := range testCases { + collector := testutils.NewMockCollector() + mockCloudwatchClient := MockAWSCloudwatchClient{ + responseMetricStatistics: defaultResponseMetricStatistics, + } + cloutwatchManager := aws.NewCloudWatchManager(&mockCloudwatchClient) + pricingManager := aws.NewPricingManager(&defaultPricingMock, "us-east-1") + + elbv2Manager := aws.NewELBV2Manager(collector, &mockClient, cloutwatchManager, pricingManager, defaultMetricConfig, tc.region) + response, err := elbv2Manager.Detect() + t.Run(tc.region, func(t *testing.T) { + if len(response) != 0 { + t.Fatalf("unexpected elbv2 detected, got %d expected %d", len(response), 0) + } + + if len(collector.Events) != 0 { + t.Fatalf("unexpected collector elbv2 resources, got %d expected %d", len(collector.Events), 0) + } + + if len(collector.EventsCollectionStatus) != 2 { + t.Fatalf("unexpected resource status events count, got %d expected %d", len(collector.EventsCollectionStatus), 2) + } + if !errors.Is(err, tc.expectedError) { + t.Fatalf("unexpected error response, got: %v, expected: %v", err, tc.expectedError) + } + }) } - } diff --git a/collector/aws/pricing.go b/collector/aws/pricing.go index 9cb9d4fd..087e7fd9 100644 --- a/collector/aws/pricing.go +++ b/collector/aws/pricing.go @@ -19,32 +19,41 @@ const ( defaultRateCode = "6YS6EN2CT7" ) -var regionToLocation = 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)", - "ap-east-1": "Asia Pacific (Hong Kong)", - "ap-south-1": "Asia Pacific (Mumbai)", - "ap-northeast-3": "Asia Pacific (Osaka-Local)", - "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)", - "cn-north-1": "China (Beijing)", - "cn-northwest-1": "China (Ningxia)", - "eu-central-1": "EU (Frankfurt)", - "eu-west-1": "EU (Ireland)", - "eu-west-2": "EU (London)", - "eu-west-3": "EU (Paris)", - "eu-south-1": "EU (Milan)", - "eu-north-1": "EU (Stockholm)", - "sa-east-1": "South America (Sao Paulo)", - "us-gov-east-1": "AWS GovCloud (US-East)", - "us-gov-west-1": "AWS GovCloud (US)", - "af-south-1": "Africa (Cape Town)", - "me-south-1": "Middle East (Bahrain)", +// ErrRegionNotFound when a region is not found +var ErrRegionNotFound = errors.New("region was not found as part of the regionsInfo map") + +// regionInfo will hold data about a region pricing options +type regionInfo struct { + fullName string + prefix string +} + +var regionsInfo = map[string]regionInfo{ + "us-east-2": {fullName: "US East (Ohio)", prefix: "USE2"}, + "us-east-1": {fullName: "US East (N. Virginia)", prefix: ""}, + "us-west-1": {fullName: "US West (N. California)", prefix: "USW1"}, + "us-west-2": {fullName: "US West (Oregon)", prefix: "USW2"}, + "ap-east-1": {fullName: "Asia Pacific (Hong Kong)", prefix: "APE1"}, + "ap-south-1": {fullName: "Asia Pacific (Mumbai)", prefix: ""}, + "ap-northeast-3": {fullName: "Asia Pacific (Osaka-Local)", prefix: "APN3"}, + "ap-northeast-2": {fullName: "Asia Pacific (Seoul)", prefix: "APN2"}, + "ap-southeast-1": {fullName: "Asia Pacific (Singapore)", prefix: "APS1"}, + "ap-southeast-2": {fullName: "Asia Pacific (Sydney)", prefix: "APS2"}, + "ap-northeast-1": {fullName: "Asia Pacific (Tokyo)", prefix: "APN1"}, + "ca-central-1": {fullName: "Canada (Central)", prefix: "CAN1"}, + "cn-north-1": {fullName: "China (Beijing)", prefix: ""}, + "cn-northwest-1": {fullName: "China (Ningxia)", prefix: ""}, + "eu-central-1": {fullName: "EU (Frankfurt)", prefix: "EUC1"}, + "eu-west-1": {fullName: "EU (Ireland)", prefix: "EUW1"}, + "eu-west-2": {fullName: "EU (London)", prefix: "EUW2"}, + "eu-west-3": {fullName: "EU (Paris)", prefix: "EUW3"}, + "eu-south-1": {fullName: "EU (Milan)", prefix: "EUS1"}, + "eu-north-1": {fullName: "EU (Stockholm)", prefix: "EUN1"}, + "sa-east-1": {fullName: "South America (Sao Paulo)", prefix: "SAE1"}, + "us-gov-east-1": {fullName: "AWS GovCloud (US-East)", prefix: "UGE1"}, + "us-gov-west-1": {fullName: "AWS GovCloud (US)", prefix: "UGW1"}, + "af-south-1": {fullName: "Africa (Cape Town)", prefix: "AFS1"}, + "me-south-1": {fullName: "Middle East (Bahrain)", prefix: "MES1"}, } // PricingClientDescreptor is an interface defining the aws pricing client @@ -95,7 +104,7 @@ type PriceCurrencyCode struct { // NewPricingManager implements AWS GO SDK func NewPricingManager(client PricingClientDescreptor, region string) *PricingManager { - log.Debug("Init aws pricing SDK client") + log.Debug("Initializing aws pricing SDK client") return &PricingManager{ client: client, region: region, @@ -103,22 +112,24 @@ func NewPricingManager(client PricingClientDescreptor, region string) *PricingMa } } -// GetPrice return the product price by given product filter -// The result (of the given product input) should be only one product. +// GetPrice returns the product price filtered by product filters +// The result (of the given product input) should be only one product as a specific product with specific usage +// Should have only 1 price to calculate total price func (p *PricingManager) GetPrice(input *pricing.GetProductsInput, rateCode string, region string) (float64, error) { if rateCode == "" { rateCode = defaultRateCode } - location, found := regionToLocation[region] + + regionInfo, found := regionsInfo[region] if !found { - return 0, errors.New("Given region not found") + return 0, ErrRegionNotFound } input.Filters = append(input.Filters, &pricing.Filter{ Type: awsClient.String("TERM_MATCH"), Field: awsClient.String("location"), - Value: awsClient.String(location), + Value: awsClient.String(regionInfo.fullName), }) hash, err := hashstructure.Hash(input, nil) @@ -144,8 +155,7 @@ func (p *PricingManager) GetPrice(input *pricing.GetProductsInput, rateCode stri "search_query": input, "products": len(priceResponse.PriceList), }).Error("Price list response should be equal to 1 product") - - return 0, errors.New("Pricelice response should be equal to 1 product") + return 0, errors.New("Price list response should be equal only to 1 product") } product := priceResponse.PriceList[0] @@ -155,7 +165,7 @@ func (p *PricingManager) GetPrice(input *pricing.GetProductsInput, rateCode stri log.WithError(err).WithFields(log.Fields{ "search_query": input, "product": product, - }).Error("could not encoded JSON value") + }).Error("could not encode JSON value") return 0, err } @@ -177,7 +187,7 @@ func (p *PricingManager) GetPrice(input *pricing.GetProductsInput, rateCode stri log.WithError(err).WithFields(log.Fields{ "search_query": input, "product": product, - }).Error("could not pare USD price from string to float64") + }).Error("could not parse USD price from string to float64") return 0, err } @@ -189,3 +199,23 @@ func (p *PricingManager) GetPrice(input *pricing.GetProductsInput, rateCode stri }).Debug("AWS resource price was found") return price, nil } + +// GetRegionPrefix will return the prefix for a +// pricing filter value according to a given region. +// For example: +// Region: "us-east-2" prefix will be: "USE2-" +func (p *PricingManager) GetRegionPrefix(region string) (string, error) { + var prefix string + regionInfo, found := regionsInfo[region] + if !found { + return prefix, ErrRegionNotFound + } + + switch regionInfo.prefix { + case "": + prefix = "" + default: + prefix = fmt.Sprintf("%s-", regionsInfo[region].prefix) + } + return prefix, nil +} diff --git a/collector/aws/pricing_test.go b/collector/aws/pricing_test.go index 46a2cfb2..38c1701b 100644 --- a/collector/aws/pricing_test.go +++ b/collector/aws/pricing_test.go @@ -98,3 +98,29 @@ func TestGetPrice(t *testing.T) { }) } + +func TestGetRegionPrefix(t *testing.T) { + testCases := []struct { + region string + expectedPrefix string + expectedError error + }{ + {"us-east-1", "", nil}, + {"us-west-1", "USW1-", nil}, + {"ap-northeast-2", "APN2-", nil}, + {"bla", "", aws.ErrRegionNotFound}, + } + + pricingManager := aws.NewPricingManager(&defaultPricingMock, "us-east-1") + for _, tc := range testCases { + t.Run(tc.region, func(t *testing.T) { + pricingValuePrefix, err := pricingManager.GetRegionPrefix(tc.region) + if tc.expectedPrefix != pricingValuePrefix { + t.Fatalf("unexpected pricing value prefix, got: %s, expected: %s", pricingValuePrefix, tc.expectedPrefix) + } + if err != tc.expectedError { + t.Fatalf("unexpected error response, got: %v, expected: %v", err, tc.expectedError) + } + }) + } +}