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
14 changes: 11 additions & 3 deletions providers/azure/services/cache/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,17 @@ func (c *CacheClient) PurchaseCommitment(ctx context.Context, rec common.Recomme
"billingScopeId": fmt.Sprintf("/subscriptions/%s", c.subscriptionID),
"term": fmt.Sprintf("P%dY", termYears),
"quantity": rec.Count,
"displayName": fmt.Sprintf("Redis Cache Reservation - %s", rec.ResourceType),
"appliedScopeType": "Shared",
"renew": false,
"displayName": reservations.BuildDisplayName(reservations.DisplayNameFields{
Service: "redis",
Region: c.region,
ResourceType: rec.ResourceType,
Count: rec.Count,
Term: rec.Term,
Payment: rec.PaymentOption,
Now: time.Now(),
}),
"appliedScopeType": "Shared",
"renew": false,
},
}
applyPurchaseAutomationTag(requestBody, opts.Source)
Expand Down
51 changes: 51 additions & 0 deletions providers/azure/services/cache/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1145,3 +1145,54 @@ func TestCacheClient_PurchaseCommitment_RequiresSource(t *testing.T) {
// No HTTP call may be issued when the guard rejects the request.
mockHTTP.AssertNotCalled(t, "Do", mock.Anything)
}

// TestCacheClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist guards
// against regression: displayName in the calculatePrice body must match
// [A-Za-z0-9_-]{1,64} (Azure rejects DisplayNameInvalid otherwise).
func TestCacheClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist(t *testing.T) {
ctx := context.Background()
mockHTTP := &MockHTTPClient{}
mockCred := &MockTokenCredential{token: "test-token"}
client := NewClientWithHTTP(mockCred, "test-subscription", "eastus", mockHTTP)

const orderID = "azure-cache-displayname"
var capturedDisplayName string
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
if r.Method != http.MethodPost || r.URL.Path != "/providers/Microsoft.Capacity/calculatePrice" {
return false
}
if r.Body == nil {
return true
}
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewReader(bodyBytes))
var body map[string]interface{}
if err := json.Unmarshal(bodyBytes, &body); err == nil {
if props, ok := body["properties"].(map[string]interface{}); ok {
if dn, ok := props["displayName"].(string); ok {
capturedDisplayName = dn
}
}
}
return true
})).Return(createMockHTTPResponse(http.StatusOK, calcPriceRespJSON(orderID)), nil).Once()
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
return r.Method == http.MethodPost &&
r.URL.Path == "/providers/Microsoft.Capacity/reservationOrders/"+orderID+"/purchase"
})).Return(createMockHTTPResponse(http.StatusOK, `{}`), nil).Once()

rec := common.Recommendation{
ResourceType: "Premium_P1",
Term: "1yr",
Count: 1,
CommitmentCost: 1000.0,
}
_, err := client.PurchaseCommitment(ctx, rec, common.PurchaseOptions{Source: common.PurchaseSourceCLI})
require.NoError(t, err)
assert.NotEmpty(t, capturedDisplayName)
assert.Regexp(t, `^[A-Za-z0-9_-]{1,64}$`, capturedDisplayName)
// Rich-format guards: service code is correct and SKU is preserved
// (see providers/azure/services/internal/reservations/displayname.go).
assert.Regexp(t, `^redis-`, capturedDisplayName)
assert.Contains(t, capturedDisplayName, "Premium_P1")
}
14 changes: 11 additions & 3 deletions providers/azure/services/compute/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,17 @@ func (c *ComputeClient) buildReservationBody(rec common.Recommendation, source s
"billingScopeId": fmt.Sprintf("/subscriptions/%s", c.subscriptionID),
"term": fmt.Sprintf("P%dY", termYears),
"quantity": rec.Count,
"displayName": fmt.Sprintf("VM Reservation - %s", rec.ResourceType),
"appliedScopeType": "Shared",
"renew": false,
"displayName": reservations.BuildDisplayName(reservations.DisplayNameFields{
Service: "vm",
Region: c.region,
ResourceType: rec.ResourceType,
Count: rec.Count,
Term: rec.Term,
Payment: rec.PaymentOption,
Now: time.Now(),
}),
"appliedScopeType": "Shared",
"renew": false,
},
}
if source != "" {
Expand Down
53 changes: 53 additions & 0 deletions providers/azure/services/compute/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1064,3 +1064,56 @@ func TestComputeClient_CachedSKULookup_FetchedOnce(t *testing.T) {
}
assert.Equal(t, 1, mockPager.pageHits, "catalogue must be fetched ONCE regardless of lookup count")
}

// TestComputeClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist guards
// against regression: displayName in the calculatePrice body must match
// [A-Za-z0-9_-]{1,64} (Azure rejects DisplayNameInvalid otherwise).
func TestComputeClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist(t *testing.T) {
ctx := context.Background()
mockHTTP := &mocks.MockHTTPClient{}
mockCred := &MockTokenCredential{token: "test-token"}
client := NewClientWithHTTP(mockCred, "test-subscription", "eastus", mockHTTP)

mockCapacityProviderCheck(mockHTTP)

const orderID = "azure-vm-displayname"
var capturedDisplayName string
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
if r.Method != http.MethodPost || r.URL.Path != "/providers/Microsoft.Capacity/calculatePrice" {
return false
}
if r.Body == nil {
return true
}
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewReader(bodyBytes))
var body map[string]interface{}
if err := json.Unmarshal(bodyBytes, &body); err == nil {
if props, ok := body["properties"].(map[string]interface{}); ok {
if dn, ok := props["displayName"].(string); ok {
capturedDisplayName = dn
}
}
}
return true
})).Return(mocks.CreateMockHTTPResponse(http.StatusOK, calcPriceRespJSON(orderID)), nil).Once()
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
return r.Method == http.MethodPost &&
r.URL.Path == "/providers/Microsoft.Capacity/reservationOrders/"+orderID+"/purchase"
})).Return(mocks.CreateMockHTTPResponse(http.StatusOK, `{}`), nil).Once()

rec := common.Recommendation{
ResourceType: "Standard_D2s_v3",
Term: "1yr",
Count: 1,
CommitmentCost: 2000.0,
}
_, err := client.PurchaseCommitment(ctx, rec, common.PurchaseOptions{Source: common.PurchaseSourceCLI})
require.NoError(t, err)
assert.NotEmpty(t, capturedDisplayName)
assert.Regexp(t, `^[A-Za-z0-9_-]{1,64}$`, capturedDisplayName)
// Rich-format guards: service code is correct and SKU is preserved
// (see providers/azure/services/internal/reservations/displayname.go).
assert.Regexp(t, `^vm-`, capturedDisplayName)
assert.Contains(t, capturedDisplayName, "Standard_D2s_v3")
}
14 changes: 11 additions & 3 deletions providers/azure/services/cosmosdb/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,17 @@ func (c *CosmosDBClient) PurchaseCommitment(ctx context.Context, rec common.Reco
"billingScopeId": fmt.Sprintf("/subscriptions/%s", c.subscriptionID),
"term": fmt.Sprintf("P%dY", termYears),
"quantity": rec.Count,
"displayName": fmt.Sprintf("Cosmos DB Reservation - %s", rec.ResourceType),
"appliedScopeType": "Shared",
"renew": false,
"displayName": reservations.BuildDisplayName(reservations.DisplayNameFields{
Service: "cosmos",
Region: c.region,
ResourceType: rec.ResourceType,
Count: rec.Count,
Term: rec.Term,
Payment: rec.PaymentOption,
Now: time.Now(),
}),
"appliedScopeType": "Shared",
"renew": false,
},
}
applyPurchaseAutomationTag(requestBody, opts.Source)
Expand Down
51 changes: 51 additions & 0 deletions providers/azure/services/cosmosdb/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1214,3 +1214,54 @@ func TestDetailsFromCosmosSKU(t *testing.T) {
})
}
}

// TestCosmosDBClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist guards
// against regression: displayName in the calculatePrice body must match
// [A-Za-z0-9_-]{1,64} (Azure rejects DisplayNameInvalid otherwise).
func TestCosmosDBClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist(t *testing.T) {
ctx := context.Background()
mockHTTP := &MockHTTPClient{}
mockCred := &MockTokenCredential{token: "test-token"}
client := NewClientWithHTTP(mockCred, "test-subscription", "eastus", mockHTTP)

const orderID = "azure-cosmos-displayname"
var capturedDisplayName string
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
if r.Method != http.MethodPost || r.URL.Path != "/providers/Microsoft.Capacity/calculatePrice" {
return false
}
if r.Body == nil {
return true
}
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewReader(bodyBytes))
var body map[string]interface{}
if err := json.Unmarshal(bodyBytes, &body); err == nil {
if props, ok := body["properties"].(map[string]interface{}); ok {
if dn, ok := props["displayName"].(string); ok {
capturedDisplayName = dn
}
}
}
return true
})).Return(createMockHTTPResponse(http.StatusOK, calcPriceRespJSON(orderID)), nil).Once()
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
return r.Method == http.MethodPost &&
r.URL.Path == "/providers/Microsoft.Capacity/reservationOrders/"+orderID+"/purchase"
})).Return(createMockHTTPResponse(http.StatusOK, `{}`), nil).Once()

rec := common.Recommendation{
ResourceType: "EnableCassandra",
Term: "1yr",
Count: 100,
CommitmentCost: 5000.0,
}
_, err := client.PurchaseCommitment(ctx, rec, common.PurchaseOptions{Source: common.PurchaseSourceCLI})
require.NoError(t, err)
assert.NotEmpty(t, capturedDisplayName)
assert.Regexp(t, `^[A-Za-z0-9_-]{1,64}$`, capturedDisplayName)
// Rich-format guards: service code is correct and SKU is preserved
// (see providers/azure/services/internal/reservations/displayname.go).
assert.Regexp(t, `^cosmos-`, capturedDisplayName)
assert.Contains(t, capturedDisplayName, "EnableCassandra")
}
14 changes: 11 additions & 3 deletions providers/azure/services/database/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,17 @@ func (c *DatabaseClient) PurchaseCommitment(ctx context.Context, rec common.Reco
"billingScopeId": fmt.Sprintf("/subscriptions/%s", c.subscriptionID),
"term": fmt.Sprintf("P%dY", termYears),
"quantity": rec.Count,
"displayName": fmt.Sprintf("SQL DB Reservation - %s", rec.ResourceType),
"appliedScopeType": "Shared",
"renew": false,
"displayName": reservations.BuildDisplayName(reservations.DisplayNameFields{
Service: "sql",
Region: c.region,
ResourceType: rec.ResourceType,
Count: rec.Count,
Term: rec.Term,
Payment: rec.PaymentOption,
Now: time.Now(),
}),
"appliedScopeType": "Shared",
"renew": false,
},
}
applyPurchaseAutomationTag(requestBody, opts.Source)
Expand Down
51 changes: 51 additions & 0 deletions providers/azure/services/database/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1141,3 +1141,54 @@ func TestDatabaseClient_ValidateOffering_CaseInsensitive(t *testing.T) {
assert.NoError(t, err)
})
}

// TestDatabaseClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist guards
// against regression: displayName in the calculatePrice body must match
// [A-Za-z0-9_-]{1,64} (Azure rejects DisplayNameInvalid otherwise).
func TestDatabaseClient_PurchaseCommitment_DisplayNameConformsToAzureAllowlist(t *testing.T) {
ctx := context.Background()
mockHTTP := &MockHTTPClient{}
mockCred := &MockTokenCredential{token: "test-token"}
client := NewClientWithHTTP(mockCred, "test-subscription", "eastus", mockHTTP)

const orderID = "azure-db-displayname"
var capturedDisplayName string
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
if r.Method != http.MethodPost || r.URL.Path != "/providers/Microsoft.Capacity/calculatePrice" {
return false
}
if r.Body == nil {
return true
}
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewReader(bodyBytes))
var body map[string]interface{}
if err := json.Unmarshal(bodyBytes, &body); err == nil {
if props, ok := body["properties"].(map[string]interface{}); ok {
if dn, ok := props["displayName"].(string); ok {
capturedDisplayName = dn
}
}
}
return true
})).Return(createMockHTTPResponse(http.StatusOK, calcPriceRespJSON(orderID)), nil).Once()
mockHTTP.On("Do", mock.MatchedBy(func(r *http.Request) bool {
return r.Method == http.MethodPost &&
r.URL.Path == "/providers/Microsoft.Capacity/reservationOrders/"+orderID+"/purchase"
})).Return(createMockHTTPResponse(http.StatusOK, `{}`), nil).Once()

rec := common.Recommendation{
ResourceType: "GP_Gen5_2",
Term: "1yr",
Count: 1,
CommitmentCost: 1500.0,
}
_, err := client.PurchaseCommitment(ctx, rec, common.PurchaseOptions{Source: common.PurchaseSourceCLI})
require.NoError(t, err)
assert.NotEmpty(t, capturedDisplayName)
assert.Regexp(t, `^[A-Za-z0-9_-]{1,64}$`, capturedDisplayName)
// Rich-format guards: service code is correct and SKU is preserved
// (see providers/azure/services/internal/reservations/displayname.go).
assert.Regexp(t, `^sql-`, capturedDisplayName)
assert.Contains(t, capturedDisplayName, "GP_Gen5_2")
}
Loading
Loading