Skip to content

Sweep sibling enum-as-string literals in Azure purchase bodies (appliedScopeType, term) -- companion to ARCH-01 #22

Description

@cristim

Context

Adversarial review of PR LeanerCloud/cloud-commitments-cli#1208 (ARCH-01) swept all Azure reservation purchase bodies in providers/azure/services/{cache,compute,cosmosdb,database,managedredis,search,synapse}/client.go and found two more enum-as-string anti-patterns living in the same lines as the reservedResourceType fixes, identical in shape:

1. appliedScopeType: "Shared" (7 services)

Every purchase body sets "appliedScopeType": "Shared" as a raw literal. The Azure SDK defines a typed enum:

// armreservations@v1.1.0/constants.go:18-22
type AppliedScopeType string
const (
    AppliedScopeTypeShared AppliedScopeType = "Shared"
    AppliedScopeTypeSingle AppliedScopeType = "Single"
)

Same risk shape as ARCH-01: a typo in any of the 7 occurrences (e.g. lowercase "shared", plural "Shareds", capitalisation drift) would silently break the purchase request once the LeanerCloud/cloud-commitments-cli#731 role fix makes non-VM purchases reachable.

2. term: fmt.Sprintf("P%dY", termYears) (7 services)

Every purchase body builds the term string by hand. The SDK enum:

// armreservations@v1.1.0/constants.go:473-478
type ReservationTerm string
const (
    ReservationTermP1Y ReservationTerm = "P1Y"
    ReservationTermP3Y ReservationTerm = "P3Y"
    ReservationTermP5Y ReservationTerm = "P5Y"
)

Today this is fail-loud-safe because reservations.ParseTermYears only accepts 1 or 3 and the format string mechanically produces "P1Y" / "P3Y". But if someone ever extends ParseTermYears to accept 2 years or 10 years, the format string would happily produce "P2Y" / "P10Y" — invalid enum values Azure would reject. Today the calculation is correct only by accident.

Locations to sweep

providers/azure/services/cache/client.go:326,337
providers/azure/services/compute/client.go:433,444
providers/azure/services/cosmosdb/client.go:319,330
providers/azure/services/database/client.go:324,335
providers/azure/services/managedredis/client.go:266,269
providers/azure/services/search/client.go:304,315
providers/azure/services/synapse/client.go:274,277

Recommendation

  • Replace "Shared" with string(armreservations.AppliedScopeTypeShared) across the 7 services.
  • Replace fmt.Sprintf("P%dY", termYears) with a typed helper that returns the SDK constant (e.g. add reservations.AzureReservationTerm(termYears) (armreservations.ReservationTerm, error) returning ReservationTermP1Y / ReservationTermP3Y / ReservationTermP5Y, fail-loud on anything else). Use the same helper at the 7 call sites.
  • Extend the existing TestDatabaseClient_PurchaseCommitment_CanonicalReservedResourceType / TestPurchaseCommitment_canonicalReservedResourceType regression tests to also assert appliedScopeType and term against PossibleAppliedScopeTypeValues() / PossibleReservationTermValues().

Why this matters

The whole point of ARCH-01 (PR LeanerCloud/cloud-commitments-cli#1208) and the project memory entry feedback_sdk_enum_string_literals.md is "any string literal that maps to a typed enum field MUST be the exact SDK enum member, derived from the SDK". appliedScopeType and term sit on the same map literal as the reservedResourceType that LeanerCloud/cloud-commitments-cli#1208 fixes; leaving them as raw strings is the same latent failure mode, just one PR away.

Out of scope for PR LeanerCloud/cloud-commitments-cli#1208

Per the closing issue (LeanerCloud/cloud-commitments-cli#1189) and the PR description, ARCH-01 is explicitly scoped to reservedResourceType. These sibling enums are a clean follow-up.

Surfaced by

PR LeanerCloud/cloud-commitments-cli#1208 adversarial review sweep.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions