Skip to content

Commit 84a884e

Browse files
authored
fix(cmd): match Savings Plans region filters on Details.Region (#1881)
The provider-side half of #1582 was already merged. This closes the CLI half, which the issue names explicitly and which was left untouched. AWS Savings Plans recommendations never populate the top-level `Region`. `providers/aws/recommendations/parser_sp.go` sets it only inside the nested `Details: &common.SavingsPlanDetails{...}` struct. `passesDimensionFilters` was calling `shouldIncludeRegion(rec.Region, cfg)` with that bare field, which is `""` for every SP recommendation, so `--include-regions` silently dropped all of them and `--exclude-regions` silently let them all through. The user saw fewer recommendations, or the wrong ones, with no explanation either way. ## What changed `passesDimensionFilters` routes through a four-line helper that asks `IsRegionAgnostic` first and otherwise matches on `EffectiveRegion`. Those two predicates already existed on the provider side and are reused rather than reimplemented; they were unexported, so the change exports them, which is a pure identifier rename with no logic touched. `cmd` already imports `providers/aws`, so no new dependency edge. Moving them to `pkg/common` was rejected because `isAccountLevelSPPlanType` compares against `sptypes.SavingsPlanType` SDK members and the `pkg/` module carries no `savingsplans` dependency. **Empty is not region-agnostic.** The fix deliberately does not make a blank `Region` match every filter. A genuinely region-agnostic Savings Plan (Compute, SageMaker, Database) and an EC2Instance plan whose region failed to parse are different things, and only the first is exempt. `IsRegionAgnostic` decides that on positive plan-type evidence, so the fix cannot turn a silent drop into a silent over-inclusion, which on a purchasing tool is the worse failure. Exporting those helpers widened what they can be handed, so the second commit confines them to `common.ProviderAWS`. While package-private they only ever saw recommendations the AWS parsers built, and "these Details are AWS Savings Plans Details" held by construction; exported, the invariant rested on no caller making that mistake. `common.SavingsPlanDetails` and `common.CommitmentSavingsPlan` are shared types, and Azure's savings plans client type-asserts on the former in three places, so a non-AWS recommendation carrying those fields would have been read with the AWS meaning. Not reachable today: no Azure code constructs a `common.Recommendation` with `CommitmentSavingsPlan`, the only non-AWS assignment of that constant being on a `common.Commitment`, a different type. The guard restores an invariant this change weakened rather than defending a reachable state, and it sits in the helpers so every caller inherits it instead of just the one call site. ## How it was verified `TestApplyFilters_SavingsPlansRegionFilters` drives seven cases through the real `applyFilters` path. With the production line reverted it fails three ways: an in-region EC2Instance SP dropped by `--include-regions`, a region-agnostic Compute SP dropped by `--include-regions`, and an EC2Instance SP leaking past `--exclude-regions`. The four already-correct cases pin the over-inclusion trap, since an EC2Instance SP with an empty `Details.Region` is region-scoped, not agnostic, and stays dropped. `TestRegionHelpers_NonAWSRecommendation` pins the provider gate, and each half was checked for teeth independently rather than together: removing only the `IsRegionAgnostic` guard fails `IsRegionAgnostic_is_false_for_a_non-AWS_rec`, and removing only the `EffectiveRegion` guard fails `EffectiveRegion_does_not_read_Details_of_a_non-AWS_rec`. Tested jointly, the first assertion aborting would have masked whether the second was pinned at all. `go build ./...`, the full `cmd` package, the `providers/aws` module, `go vet` and `gofmt` all clean. `gocyclo -over 10` clean, confirmed to be scanning by a `-over 6` run reporting 134 functions. `passesDimensionFilters` stays at complexity 5. The first CI attempt failed in `lint` before analysing any code: `golangci-lint config verify` fetches its JSON schema over the network and the request timed out. Re-run, green. ## Sibling dimension filters: checked, neither is the same defect **Instance type** does drop SP recommendations, because `ResourceType` is empty. But `SavingsPlanDetails` has no instance type; it has `InstanceFamily` (`"m5"`), a different granularity, only for EC2Instance plans. Matching one against the other needs a semantics decision this issue does not specify, and there is no provider-side instance-type filter to mirror. Left alone deliberately. **Engine** has no bare-field bug: `SavingsPlanDetails` carries no engine attribute, because Cost Explorer returns none for any SP plan type. ## Noted, not fixed `shouldIncludeRegion` does not skip blank entries the way the provider's `regionSet` does, so `--include-regions "us-east-1,"` can admit an unknown-region recommendation. Pre-existing, affects all recommendation types, and strictly improved for Savings Plans by this change. Not verified: no live AWS run, no credentials in this environment. Closes #1582
1 parent ae1e632 commit 84a884e

4 files changed

Lines changed: 210 additions & 17 deletions

File tree

cmd/multi_service_filters.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/LeanerCloud/CUDly/pkg/common"
10+
awsprovider "github.com/LeanerCloud/CUDly/providers/aws"
1011
)
1112

1213
// applyFilters applies region, instance type, engine, and engine version filters to recommendations.
@@ -86,7 +87,7 @@ func processRecommendation(rec *common.Recommendation, cfg *Config, instanceVers
8687
// dimension filters here are pure functions of rec + cfg with no side
8788
// effects. Pool-size filtering is handled with logging in applyFilters.
8889
func passesDimensionFilters(rec *common.Recommendation, cfg *Config) bool {
89-
if !shouldIncludeRegion(rec.Region, cfg) {
90+
if !shouldIncludeRecommendationRegion(rec, cfg) {
9091
return false
9192
}
9293
if !shouldIncludeInstanceType(rec.ResourceType, cfg) {
@@ -122,6 +123,25 @@ func shouldIncludePoolSize(rec *common.Recommendation, cfg *Config) bool {
122123
return rec.AverageInstancesUsedPerHour >= cfg.MinPoolSize
123124
}
124125

126+
// shouldIncludeRecommendationRegion applies the region filters to a whole
127+
// recommendation rather than to its bare Region field. Savings Plans
128+
// recommendations leave the top-level Region empty and carry the
129+
// EC2Instance-scoped region in Details instead, so matching on rec.Region
130+
// alone dropped every SP recommendation under --include-regions and leaked
131+
// region-scoped ones past --exclude-regions (#1582).
132+
//
133+
// Both predicates are the provider package's, which already filters AWS
134+
// recommendations on these semantics, rather than a second implementation.
135+
// Recommendations from other providers are unaffected: both predicates are
136+
// gated on common.CommitmentSavingsPlan and fall through to the plain
137+
// rec.Region comparison for everything else.
138+
func shouldIncludeRecommendationRegion(rec *common.Recommendation, cfg *Config) bool {
139+
if awsprovider.IsRegionAgnostic(*rec) {
140+
return true
141+
}
142+
return shouldIncludeRegion(awsprovider.EffectiveRegion(*rec), cfg)
143+
}
144+
125145
// shouldIncludeRegion checks if a region should be included based on filters.
126146
func shouldIncludeRegion(region string, cfg *Config) bool {
127147
// If include list is specified, region must be in it.

cmd/multi_service_filters_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,3 +528,104 @@ func TestApplyFilters_DropExtendedSupport(t *testing.T) {
528528
assert.Contains(t, d.FormatOneLine(), common.DropExtendedSupport,
529529
"drop summary should name the --include-extended-support category")
530530
}
531+
532+
// TestApplyFilters_SavingsPlansRegionFilters covers the CLI half of #1582.
533+
// Savings Plans recommendations never populate the top-level rec.Region
534+
// (parser_sp.go stores the CE-supplied region in Details.Region instead), so
535+
// filtering on the bare field dropped every SP recommendation whenever
536+
// --include-regions was set, and leaked region-scoped EC2Instance SPs past
537+
// --exclude-regions. The provider-side filters were fixed first; these cases
538+
// pin the same semantics on the CLI path.
539+
func TestApplyFilters_SavingsPlansRegionFilters(t *testing.T) {
540+
ec2SP := func(region string) common.Recommendation {
541+
return common.Recommendation{
542+
Provider: common.ProviderAWS,
543+
Service: common.ServiceSavingsPlansEC2Instance,
544+
CommitmentType: common.CommitmentSavingsPlan,
545+
Count: 1,
546+
Details: &common.SavingsPlanDetails{
547+
PlanType: "EC2Instance",
548+
InstanceFamily: "m5",
549+
Region: region,
550+
},
551+
}
552+
}
553+
computeSP := func() common.Recommendation {
554+
return common.Recommendation{
555+
Provider: common.ProviderAWS,
556+
Service: common.ServiceSavingsPlansCompute,
557+
CommitmentType: common.CommitmentSavingsPlan,
558+
Count: 1,
559+
Details: &common.SavingsPlanDetails{PlanType: "Compute"},
560+
}
561+
}
562+
563+
tests := []struct {
564+
name string
565+
rec common.Recommendation
566+
includeRegions []string
567+
excludeRegions []string
568+
wantKept bool
569+
}{
570+
{
571+
name: "EC2Instance SP in the included region survives",
572+
rec: ec2SP("us-east-1"),
573+
includeRegions: []string{"us-east-1"},
574+
wantKept: true,
575+
},
576+
{
577+
name: "EC2Instance SP outside the included region is dropped",
578+
rec: ec2SP("eu-west-1"),
579+
includeRegions: []string{"us-east-1"},
580+
wantKept: false,
581+
},
582+
{
583+
name: "region-agnostic Compute SP survives an include filter",
584+
rec: computeSP(),
585+
includeRegions: []string{"us-east-1"},
586+
wantKept: true,
587+
},
588+
{
589+
name: "EC2Instance SP in an excluded region is dropped",
590+
rec: ec2SP("eu-west-1"),
591+
excludeRegions: []string{"eu-west-1"},
592+
wantKept: false,
593+
},
594+
{
595+
name: "region-agnostic Compute SP survives an exclude filter",
596+
rec: computeSP(),
597+
excludeRegions: []string{"eu-west-1"},
598+
wantKept: true,
599+
},
600+
{
601+
// Cost Explorer omitted SavingsPlansDetails.Region. An EC2Instance
602+
// SP is region-scoped, so an unknown region must not be treated as
603+
// region-agnostic and waved past an explicit region filter.
604+
name: "EC2Instance SP with an unknown region is not over-included",
605+
rec: ec2SP(""),
606+
includeRegions: []string{"us-east-1"},
607+
wantKept: false,
608+
},
609+
{
610+
name: "reservation rec with an unknown region is still dropped",
611+
rec: common.Recommendation{Service: common.ServiceRDS, CommitmentType: common.CommitmentReservedInstance, Count: 1},
612+
includeRegions: []string{"us-east-1"},
613+
wantKept: false,
614+
},
615+
}
616+
617+
for _, tt := range tests {
618+
t.Run(tt.name, func(t *testing.T) {
619+
cfg := Config{
620+
IncludeRegions: tt.includeRegions,
621+
ExcludeRegions: tt.excludeRegions,
622+
}
623+
want := 0
624+
if tt.wantKept {
625+
want = 1
626+
}
627+
got := applyFilters([]common.Recommendation{tt.rec}, &cfg, nil, nil, "", common.NewDropSummary())
628+
assert.Len(t, got, want, "region filter kept the wrong number of recommendations")
629+
})
630+
}
631+
}

providers/aws/service_client.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func filterByAccounts(recs []common.Recommendation, accounts []string) []common.
132132
return filtered
133133
}
134134

135-
// effectiveRegion returns the region to use for region-filter matching.
135+
// EffectiveRegion returns the region to use for region-filter matching.
136136
// Savings Plans recommendations never populate the top-level rec.Region
137137
// (GetSavingsPlansPurchaseRecommendation is account-level and carries no
138138
// region parameter -- see applyRecommendationFilters); EC2Instance Savings
@@ -142,10 +142,17 @@ func filterByAccounts(recs []common.Recommendation, accounts []string) []common.
142142
// EC2Instance SP recs on their real region instead of dropping them via an
143143
// always-empty top-level Region. Compute/SageMaker/Database SP recs are
144144
// genuinely region-agnostic (Details.Region stays "" for them), so this
145-
// still returns "" for those, and isRegionAgnostic is what decides that such
145+
// still returns "" for those, and IsRegionAgnostic is what decides that such
146146
// a rec is exempt from region filtering (see #1495).
147-
func effectiveRegion(rec common.Recommendation) string {
148-
if rec.Region != "" {
147+
//
148+
// The Details fallback is confined to ProviderAWS. common.SavingsPlanDetails
149+
// is a shared type that Azure's savingsplans client also asserts on, so
150+
// exporting this predicate (#1582) made it reachable with a non-AWS rec whose
151+
// Details mean something else. Only AWS builds Savings Plans recommendations
152+
// today and parser_sp.go stamps ProviderAWS on every one, so the gate changes
153+
// no behaviour now; it keeps the AWS-only reading from outliving that invariant.
154+
func EffectiveRegion(rec common.Recommendation) string {
155+
if rec.Region != "" || rec.Provider != common.ProviderAWS {
149156
return rec.Region
150157
}
151158
if sp, ok := rec.Details.(*common.SavingsPlanDetails); ok && sp != nil {
@@ -154,7 +161,7 @@ func effectiveRegion(rec common.Recommendation) string {
154161
return ""
155162
}
156163

157-
// isRegionAgnostic reports whether rec legitimately belongs to no single
164+
// IsRegionAgnostic reports whether rec legitimately belongs to no single
158165
// region and must therefore be exempt from both region filters: an
159166
// account-level Savings Plan (Compute/SageMaker/Database), which
160167
// GetSavingsPlansPurchaseRecommendation returns without any region because
@@ -186,8 +193,13 @@ func effectiveRegion(rec common.Recommendation) string {
186193
// unknown (nil Details, a non-SavingsPlanDetails payload, a plan type this
187194
// build has never heard of) stays region-scoped and is filtered
188195
// conservatively rather than exempted.
189-
func isRegionAgnostic(rec common.Recommendation) bool {
190-
if rec.CommitmentType != common.CommitmentSavingsPlan || effectiveRegion(rec) != "" {
196+
//
197+
// ProviderAWS is required for the same reason EffectiveRegion requires it:
198+
// this exemption is a statement about AWS Savings Plans products, and both
199+
// common.CommitmentSavingsPlan and common.SavingsPlanDetails are shared types
200+
// another provider could populate with different semantics.
201+
func IsRegionAgnostic(rec common.Recommendation) bool {
202+
if rec.Provider != common.ProviderAWS || rec.CommitmentType != common.CommitmentSavingsPlan || EffectiveRegion(rec) != "" {
191203
return false
192204
}
193205
sp, ok := rec.Details.(*common.SavingsPlanDetails)
@@ -236,14 +248,14 @@ func regionSet(regions []string) map[string]bool {
236248

237249
// filterByIncludedRegions filters recommendations to only included regions.
238250
// Region-agnostic recommendations (account-level Savings Plans -- see
239-
// isRegionAgnostic) are always kept: an include filter narrows region-scoped
251+
// IsRegionAgnostic) are always kept: an include filter narrows region-scoped
240252
// recs, it must not silently drop recs that belong to no region at all.
241253
func filterByIncludedRegions(recs []common.Recommendation, regions []string) []common.Recommendation {
242254
regionMap := regionSet(regions)
243255

244256
filtered := make([]common.Recommendation, 0, len(recs))
245257
for _, rec := range recs {
246-
if isRegionAgnostic(rec) || regionMap[effectiveRegion(rec)] {
258+
if IsRegionAgnostic(rec) || regionMap[EffectiveRegion(rec)] {
247259
filtered = append(filtered, rec)
248260
}
249261
}
@@ -253,14 +265,14 @@ func filterByIncludedRegions(recs []common.Recommendation, regions []string) []c
253265

254266
// filterByExcludedRegions filters out recommendations from excluded regions.
255267
// Region-agnostic recommendations (account-level Savings Plans -- see
256-
// isRegionAgnostic) are never excluded: they do not belong to any of the
268+
// IsRegionAgnostic) are never excluded: they do not belong to any of the
257269
// excluded regions.
258270
func filterByExcludedRegions(recs []common.Recommendation, regions []string) []common.Recommendation {
259271
regionMap := regionSet(regions)
260272

261273
filtered := make([]common.Recommendation, 0, len(recs))
262274
for _, rec := range recs {
263-
if isRegionAgnostic(rec) || !regionMap[effectiveRegion(rec)] {
275+
if IsRegionAgnostic(rec) || !regionMap[EffectiveRegion(rec)] {
264276
filtered = append(filtered, rec)
265277
}
266278
}

providers/aws/service_client_test.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,14 @@ func TestApplyRecommendationFilters_SavingsPlanRegion(t *testing.T) {
352352
accountLevelSP := common.Recommendation{
353353
Account: "111",
354354
Region: "",
355+
Provider: common.ProviderAWS,
355356
CommitmentType: common.CommitmentSavingsPlan,
356357
Details: &common.SavingsPlanDetails{PlanType: "Compute"},
357358
}
358359
ec2InstanceSP := common.Recommendation{
359360
Account: "222",
360361
Region: "",
362+
Provider: common.ProviderAWS,
361363
CommitmentType: common.CommitmentSavingsPlan,
362364
Details: &common.SavingsPlanDetails{PlanType: "EC2Instance", Region: "us-east-1"},
363365
}
@@ -410,7 +412,7 @@ func TestApplyRecommendationFilters_SavingsPlanRegion(t *testing.T) {
410412
// "us-east-1 only" filter and reach the purchase path, to be bought in
411413
// whatever region the service client resolved. This test fails on the
412414
// empty-region-means-agnostic version and passes with the
413-
// CommitmentSavingsPlan-gated isRegionAgnostic.
415+
// CommitmentSavingsPlan-gated IsRegionAgnostic.
414416
func TestApplyRecommendationFilters_RegionlessReservationNotExempt(t *testing.T) {
415417
regionlessRI := common.Recommendation{
416418
Account: "111",
@@ -453,12 +455,12 @@ func TestApplyRecommendationFilters_RegionlessReservationNotExempt(t *testing.T)
453455
// empty effective region, i.e. a region-less reservation (see
454456
// TestApplyRecommendationFilters_RegionlessReservationNotExempt for why those
455457
// exist). An account-level Savings Plan would NOT pin this: it
456-
// short-circuits on isRegionAgnostic before the map is ever consulted, so
458+
// short-circuits on IsRegionAgnostic before the map is ever consulted, so
457459
// that subtest would pass with or without the blank skip and prove nothing.
458460
func TestApplyRecommendationFilters_BlankRegionEntryIsNotAMatcher(t *testing.T) {
459461
// Not region-agnostic (a reservation), but carries no region because
460-
// Cost Explorer omitted the field. effectiveRegion is "" and
461-
// isRegionAgnostic is false, so this rec reaches the map lookup.
462+
// Cost Explorer omitted the field. EffectiveRegion is "" and
463+
// IsRegionAgnostic is false, so this rec reaches the map lookup.
462464
regionlessRI := common.Recommendation{
463465
Account: "111",
464466
CommitmentType: common.CommitmentReservedInstance,
@@ -504,13 +506,14 @@ func TestApplyRecommendationFilters_BlankRegionEntryIsNotAMatcher(t *testing.T)
504506
// in whatever region the service client resolved -- the same defect, one plan
505507
// type over.
506508
//
507-
// This test fails on the CommitmentSavingsPlan-only isRegionAgnostic and
509+
// This test fails on the CommitmentSavingsPlan-only IsRegionAgnostic and
508510
// passes once the exemption also requires isAccountLevelSPPlanType.
509511
func TestApplyRecommendationFilters_RegionlessEC2InstanceSPNotExempt(t *testing.T) {
510512
// An EC2Instance SP whose CE payload carried no region: region-scoped,
511513
// but with nothing to match a region filter against.
512514
regionlessEC2SP := common.Recommendation{
513515
Account: "111",
516+
Provider: common.ProviderAWS,
514517
CommitmentType: common.CommitmentSavingsPlan,
515518
Details: &common.SavingsPlanDetails{PlanType: "EC2Instance"},
516519
}
@@ -532,6 +535,7 @@ func TestApplyRecommendationFilters_RegionlessEC2InstanceSPNotExempt(t *testing.
532535
for _, planType := range []string{"Compute", "SageMaker", "Database"} {
533536
accountLevelSP := common.Recommendation{
534537
Account: "111",
538+
Provider: common.ProviderAWS,
535539
CommitmentType: common.CommitmentSavingsPlan,
536540
Details: &common.SavingsPlanDetails{PlanType: planType},
537541
}
@@ -548,6 +552,7 @@ func TestApplyRecommendationFilters_RegionlessEC2InstanceSPNotExempt(t *testing.
548552
// conservative direction is to filter it, not to exempt it.
549553
unknownSP := common.Recommendation{
550554
Account: "111",
555+
Provider: common.ProviderAWS,
551556
CommitmentType: common.CommitmentSavingsPlan,
552557
Details: &common.SavingsPlanDetails{PlanType: "SomeFutureSP"},
553558
}
@@ -559,6 +564,7 @@ func TestApplyRecommendationFilters_RegionlessEC2InstanceSPNotExempt(t *testing.
559564
t.Run("an SP carrying no Details at all is not exempt", func(t *testing.T) {
560565
noDetailsSP := common.Recommendation{
561566
Account: "111",
567+
Provider: common.ProviderAWS,
562568
CommitmentType: common.CommitmentSavingsPlan,
563569
}
564570
got := applyRecommendationFilters([]common.Recommendation{noDetailsSP},
@@ -569,6 +575,7 @@ func TestApplyRecommendationFilters_RegionlessEC2InstanceSPNotExempt(t *testing.
569575
t.Run("an EC2Instance SP that DOES carry its region is filtered on that region", func(t *testing.T) {
570576
ec2SPInUsEast := common.Recommendation{
571577
Account: "111",
578+
Provider: common.ProviderAWS,
572579
CommitmentType: common.CommitmentSavingsPlan,
573580
Details: &common.SavingsPlanDetails{PlanType: "EC2Instance", Region: "us-east-1"},
574581
}
@@ -581,3 +588,56 @@ func TestApplyRecommendationFilters_RegionlessEC2InstanceSPNotExempt(t *testing.
581588
assert.Empty(t, dropped, "a non-matching region must still be dropped")
582589
})
583590
}
591+
592+
// TestRegionHelpers_NonAWSRecommendation pins the provider gate on the two
593+
// exported region helpers.
594+
//
595+
// While they were package-private they could only ever see recommendations the
596+
// AWS parsers built, so "these Details are AWS Savings Plans Details" held by
597+
// construction. Exporting them for the CLI (#1582) removed that guarantee: any
598+
// caller can now pass any recommendation. common.CommitmentSavingsPlan and
599+
// common.SavingsPlanDetails are shared types, and Azure already type-asserts on
600+
// SavingsPlanDetails in its own savingsplans client, so the shape below is one
601+
// an exported helper must handle even though no Azure code builds a Savings
602+
// Plans *recommendation* today.
603+
func TestRegionHelpers_NonAWSRecommendation(t *testing.T) {
604+
nonAWSSP := common.Recommendation{
605+
Provider: common.ProviderAzure,
606+
CommitmentType: common.CommitmentSavingsPlan,
607+
Details: &common.SavingsPlanDetails{PlanType: "Compute", Region: "eastus"},
608+
}
609+
610+
t.Run("EffectiveRegion does not read Details of a non-AWS rec", func(t *testing.T) {
611+
assert.Empty(t, EffectiveRegion(nonAWSSP),
612+
"a non-AWS recommendation must report its own Region, not an AWS-shaped reading of Details")
613+
})
614+
615+
t.Run("EffectiveRegion returns a non-AWS rec's own Region unchanged", func(t *testing.T) {
616+
withRegion := nonAWSSP
617+
withRegion.Region = "westeurope"
618+
assert.Equal(t, "westeurope", EffectiveRegion(withRegion))
619+
})
620+
621+
t.Run("IsRegionAgnostic is false for a non-AWS rec", func(t *testing.T) {
622+
// Deliberately region-less. Given a populated Details.Region the
623+
// EffectiveRegion(rec) != "" arm rejects the rec on its own and the
624+
// provider gate is never reached, so the assertion would hold with or
625+
// without the gate and prove nothing. Every other arm has to pass for
626+
// this case to have teeth: CommitmentSavingsPlan, an empty effective
627+
// region, and an account-level plan type.
628+
nonAWSAccountLevelSP := common.Recommendation{
629+
Provider: common.ProviderAzure,
630+
CommitmentType: common.CommitmentSavingsPlan,
631+
Details: &common.SavingsPlanDetails{PlanType: "Compute"},
632+
}
633+
assert.False(t, IsRegionAgnostic(nonAWSAccountLevelSP),
634+
"the account-level exemption is a statement about AWS Savings Plans products")
635+
})
636+
637+
t.Run("the same rec on ProviderAWS still gets the AWS reading", func(t *testing.T) {
638+
awsSP := nonAWSSP
639+
awsSP.Provider = common.ProviderAWS
640+
assert.Equal(t, "eastus", EffectiveRegion(awsSP),
641+
"the gate must not change behaviour for AWS recs, or it would break the #1582 fix")
642+
})
643+
}

0 commit comments

Comments
 (0)