Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions providers/azure/services/compute/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,9 +798,11 @@ func (c *ComputeClient) cachedSKULookup(ctx context.Context, skuName string) (vm
// GetValidResourceTypes — a SKU listed for a different region is not
// safe to attribute to a recommendation in this client's region).
//
// Returns nil on pager-create or page-fetch error so the
// sync.Once-gated cache field stays nil and cachedSKULookup falls back
// to the empty-fields path. The fetch error is logged WARN once.
// Returns nil on pager-create, page-fetch error, or context cancellation
// so the sync.Once-gated cache field stays nil and cachedSKULookup falls
// back to the empty-fields path. Errors and cancellation are logged WARN
// once; context.Canceled/DeadlineExceeded are treated as terminal
// (feedback_ctx_cancel_terminal.md).
func (c *ComputeClient) fetchSKUCatalogue(ctx context.Context) map[string]vmSKUEntry {
pager, err := c.createResourceSKUsPager()
if err != nil {
Expand Down
29 changes: 29 additions & 0 deletions providers/azure/services/compute/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,35 @@ func TestComputeClient_CachedSKULookup_FetchedOnce(t *testing.T) {
assert.Equal(t, 1, mockPager.pageHits, "catalogue must be fetched ONCE regardless of lookup count")
}

// TestComputeClient_FetchSKUCatalogue_CancelledContextFallsBack asserts
// that a cancelled context is terminal in the SKU catalogue pagination
// loop — the catalogue returns nil and Details.VCPU/MemoryGB stay at 0,
// but the conversion itself succeeds (graceful-degradation contract).
// Pins feedback_ctx_cancel_terminal.md for the compute SKU path.
func TestComputeClient_FetchSKUCatalogue_CancelledContextFallsBack(t *testing.T) {
client := NewClient(nil, "test-subscription", "eastus")

ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately so ctx.Err() is set on first loop iteration

mockPager := &vmSKUCatalogueMockPager{
pages: []armcompute.ResourceSKUsClientListResponse{
{
ResourceSKUsResult: armcompute.ResourceSKUsResult{
Value: []*armcompute.ResourceSKU{
buildVMSKU("Standard_D2s_v3", "eastus", 2, "8"),
},
},
},
},
}
client.SetResourceSKUsPager(mockPager)

result := client.fetchSKUCatalogue(ctx)
assert.Nil(t, result, "cancelled context must return nil catalogue")
assert.Equal(t, 0, mockPager.pageHits, "NextPage must not be called after context is already cancelled")
}

// TestComputeClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist guards
// against regression: displayName in the calculatePrice body must match
// [A-Za-z0-9_-]{1,64} (Azure rejects DisplayNameInvalid otherwise).
Expand Down
Loading