You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(azure): wire payment_option to reservation billingPlanType (Azure RIs always bill upfront) #1502
Azure Reserved Instance purchases always bill upfront regardless of the requested payment_option. The purchase body builder (providers/azure/services/compute/client.go, buildReservationBody) is called with opts.Source and opts.IdempotencyToken but never receives opts.PaymentOption, so Azure's reservation API sends no billingPlanType and defaults to upfront.
Impact
A caller requesting no-upfront or partial-upfront for an Azure RI would silently get an upfront commitment (different cash-flow schedule than requested).
Currently guarded, not honored: the CUDly MCP server (PR feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure, GCP #1495) fails loud and rejects non-all-upfront Azure RI real purchases rather than silently mis-billing. So there is no active money-safety hole; this issue is to actually support the other billing plans.
Fix
Thread opts.PaymentOption into buildReservationBody and map it to Azure's billingPlanType (Upfront vs Monthly) on the reservation purchase request. Azure reservations support Upfront and Monthly billing plans; map all-upfront -> Upfront, no-upfront/partial-upfront -> the closest supported plan (Monthly), or reject combinations Azure genuinely cannot express (fail loud, no silent default).
Use the SDK's typed billing-plan enum, not a raw string literal (project convention).
Once wired, remove the MCP tool's real-purchase rejection of non-all-upfront in mcp/tools/azure_compute_ri.go and let the value flow through.
Scope note
This is a provider-layer change that also benefits the CLI and web (internal/api) purchase paths, not just MCP. Verify against Azure's reservation billing-plan API surface before wiring; do not assume the mapping.
Scope widened: the same billingPlan omission affects 6 sibling Azure services
The compute wiring is landing in PR #1495 (new helper reservations.BillingPlanForPaymentOption + billingPlan set in compute's buildReservationBody). An adversarial review confirmed the other six Azure reservation-based services build their own request bodies and never set billingPlan, so a no-upfront/monthly recommendation for any of them is still silently billed at Azure's Upfront default:
providers/azure/services/database
providers/azure/services/cache
providers/azure/services/cosmosdb
providers/azure/services/search
providers/azure/services/managedredis
providers/azure/services/synapse
Remaining work for this issue
Wire each of the six clients' reservation-body builders through the shared reservations.BillingPlanForPaymentOption helper (same pattern PR #1495 applied to compute), with a per-service test asserting the marshaled body carries billingPlan: Monthly for no-upfront and Upfront for all-upfront, and fails loud on empty/partial-upfront. Compute is done in #1495; this issue now tracks the remaining six.
I can take the six remaining Azure service clients as an assignment-first fixed scope for 120 USDC, delivered within 48 hours after escrow funding.
Proposed acceptance checklist:
thread each service's requested payment option through the existing shared reservations.BillingPlanForPaymentOption helper for database, cache, cosmosdb, search, managedredis, and synapse;
serialize the SDK's typed billing-plan enum into each reservation purchase body — Upfront for all-upfront, Monthly for no-upfront;
preserve fail-loud behavior for empty/unsupported/partial-upfront values rather than allowing Azure's implicit Upfront default;
add a table-driven or per-service body-marshalling test that proves both supported values and the rejection path for every client;
run the affected provider tests plus go test ./..., and provide exact output;
no Azure credentials, real reservations, or production purchases required.
Additional evidence from the 2026-07-28 full-repo review (reviewed commit be11bdcb5, origin/main). Confirmed still live on origin/main; filing here rather than as a duplicate issue.
The gap is wider than "Azure RIs always bill upfront": the system actively offers and prices a billing schedule that the purchase path cannot request.
Where (all seven purchase bodies, none of which sets billingPlan):
What:rec.PaymentOption is only consumed to build the displayName string. billingPlan (armreservations.ReservationBillingPlanMonthly / ...Upfront) is never emitted in any of the seven.
The aggravating detail:ExpandPaymentVariants (providers/azure/internal/recommendations/converter.go:358-403) actively fans every recommendation into an "upfront" and a "monthly" variant, and GetOfferingDetails (e.g. cache/client.go) renders a monthly cashflow preview for the monthly variant that the purchase path cannot honor.
Failure scenario: a user picks the "monthly" variant of a 3-year VM reservation quoted at roughly $X/month. Azure defaults the omitted billingPlan to Upfront and charges the full 3-year price on day one. The approved cashflow and the actual charge differ by the entire upfront amount.
Fix direction (unchanged from this issue): map rec.PaymentOption to the armreservations.ReservationBillingPlan enum constant, send it in both the calculatePrice and purchase bodies, and fail closed on an unrecognised value.
Given that the monthly variant is surfaced and priced to the user, the severity and priority on this issue (currently severity/medium, priority/p3) may be worth revisiting.
Verified resolved at 3c0f8ac: rec.PaymentOption is mapped through reservations.BillingPlanForPaymentOption to the typed armreservations.ReservationBillingPlan enum and emitted as properties.billingPlan in the reservation body, with empty and partial-upfront values failing loud instead of silently defaulting to Upfront. Evidence: providers/azure/services/compute/client.go:499-505 and :436 (#1495, commit 887d51f). Residual axes checked: all seven sibling services (compute, cache, cosmosdb, database, managedredis, search, synapse) set billingPlan and each carries a billingPlan regression test; the same body is sent to both calculatePrice and purchase so the quoted and charged schedules agree; the MCP tool's blanket non-all-upfront rejection was lifted and only partial-upfront is now refused, which Azure genuinely cannot express; the RI-exchange path hardcodes Upfront but exposes no caller-supplied payment option, so nothing is silently overridden there. Closing as completed; reopen if the behaviour recurs.
Activity
cristim commentedon Jul 23, 2026
Scope widened: the same billingPlan omission affects 6 sibling Azure services
The compute wiring is landing in PR #1495 (new helper
reservations.BillingPlanForPaymentOption+billingPlanset in compute'sbuildReservationBody). An adversarial review confirmed the other six Azure reservation-based services build their own request bodies and never setbillingPlan, so ano-upfront/monthlyrecommendation for any of them is still silently billed at Azure's Upfront default:providers/azure/services/databaseproviders/azure/services/cacheproviders/azure/services/cosmosdbproviders/azure/services/searchproviders/azure/services/managedredisproviders/azure/services/synapseRemaining work for this issue
Wire each of the six clients' reservation-body builders through the shared
reservations.BillingPlanForPaymentOptionhelper (same pattern PR #1495 applied to compute), with a per-service test asserting the marshaled body carriesbillingPlan: Monthlyfor no-upfront andUpfrontfor all-upfront, and fails loud on empty/partial-upfront. Compute is done in #1495; this issue now tracks the remaining six.bughunter-dotcom commentedon Jul 23, 2026
I can take the six remaining Azure service clients as an assignment-first fixed scope for 120 USDC, delivered within 48 hours after escrow funding.
Proposed acceptance checklist:
reservations.BillingPlanForPaymentOptionhelper fordatabase,cache,cosmosdb,search,managedredis, andsynapse;Upfrontforall-upfront,Monthlyforno-upfront;partial-upfrontvalues rather than allowing Azure's implicit Upfront default;go test ./..., and provide exact output;Before implementation, please confirm:
bughunter-dotcom;mainor be based on open PR feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure, GCP #1495 where the shared helper and compute reference implementation currently live;Escrow option (LaborX, Base USDC): https://laborx.com/gigs/i-will-debug-and-fix-your-api-ci-or-qa-automation-with-tests-109222
Relevant deterministic test-delivery samples: https://github.com/bughunter-dotcom/resilient-http-client-sample and https://github.com/bughunter-dotcom/playwright-ci-sample
If direct fixed-USDC contracting is not available, no problem — I will not submit a speculative implementation.
fix(azure): wire billingPlan into SQL Database reservation purchases
fix(azure): wire billingPlan into Cache for Redis reservation purchases
fix(azure): wire billingPlan into Cosmos DB reservation purchases
fix(azure): wire billingPlan into Search reservation purchases
9 remaining items
docs(config): correct azure payment-coercion rationale
docs(config): future-proof Azure payment-coercion comments for #1495
docs(config): correct azure payment-coercion rationale
fix(azure): wire billingPlan into SQL Database reservation purchases
fix(azure): wire billingPlan into Cache for Redis reservation purchases
fix(azure): wire billingPlan into Cosmos DB reservation purchases
fix(azure): wire billingPlan into Search reservation purchases
fix(azure): wire billingPlan into Managed Redis reservation purchases
fix(azure): wire billingPlan into Synapse reservation purchases
fix(azure): coerce web partial-upfront to monthly, not upfront (#1503) (
cristim commentedon Jul 28, 2026
Additional evidence from the 2026-07-28 full-repo review (reviewed commit
be11bdcb5, origin/main). Confirmed still live onorigin/main; filing here rather than as a duplicate issue.The gap is wider than "Azure RIs always bill upfront": the system actively offers and prices a billing schedule that the purchase path cannot request.
Where (all seven purchase bodies, none of which sets
billingPlan):providers/azure/services/cache/client.go:305-321providers/azure/services/compute/client.go:422-438providers/azure/services/cosmosdb/client.go:306-322providers/azure/services/database/client.go:336-352providers/azure/services/managedredis/client.go:273-281providers/azure/services/search/client.go:256-272providers/azure/services/synapse/client.go:279-287What:
rec.PaymentOptionis only consumed to build thedisplayNamestring.billingPlan(armreservations.ReservationBillingPlanMonthly/...Upfront) is never emitted in any of the seven.The aggravating detail:
ExpandPaymentVariants(providers/azure/internal/recommendations/converter.go:358-403) actively fans every recommendation into an "upfront" and a "monthly" variant, andGetOfferingDetails(e.g.cache/client.go) renders a monthly cashflow preview for the monthly variant that the purchase path cannot honor.Failure scenario: a user picks the "monthly" variant of a 3-year VM reservation quoted at roughly $X/month. Azure defaults the omitted
billingPlantoUpfrontand charges the full 3-year price on day one. The approved cashflow and the actual charge differ by the entire upfront amount.Fix direction (unchanged from this issue): map
rec.PaymentOptionto thearmreservations.ReservationBillingPlanenum constant, send it in both the calculatePrice and purchase bodies, and fail closed on an unrecognised value.Given that the monthly variant is surfaced and priced to the user, the severity and priority on this issue (currently
severity/medium,priority/p3) may be worth revisiting.feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure…
cristim commentedon Sep 2, 2026
Verified resolved at 3c0f8ac: rec.PaymentOption is mapped through reservations.BillingPlanForPaymentOption to the typed armreservations.ReservationBillingPlan enum and emitted as properties.billingPlan in the reservation body, with empty and partial-upfront values failing loud instead of silently defaulting to Upfront. Evidence: providers/azure/services/compute/client.go:499-505 and :436 (#1495, commit 887d51f). Residual axes checked: all seven sibling services (compute, cache, cosmosdb, database, managedredis, search, synapse) set billingPlan and each carries a billingPlan regression test; the same body is sent to both calculatePrice and purchase so the quoted and charged schedules agree; the MCP tool's blanket non-all-upfront rejection was lifted and only partial-upfront is now refused, which Azure genuinely cannot express; the RI-exchange path hardcodes Upfront but exposes no caller-supplied payment option, so nothing is silently overridden there. Closing as completed; reopen if the behaviour recurs.
feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure…