Skip to main content

Module render

Module render 

Source
Expand description

The wire shapes — including the inconsistencies, which are the point.

UpCloud stringifies numbers inconsistently: a storage’s size is a JSON number, a server’s core_number and memory_amount are strings, a device’s storage_size is a number in one place and a string in the next, and a stop request’s timeout is a string that is accepted-and-ignored when sent as a number. monetize-cloud-impl’s Num exists only because of this. A mock that rendered everything as a number would let a client that cannot read "10" pass, and that client would then fail at the provider.

So the rendering here is hand-built serde_json rather than derived structs, and every place the real API sends a string, this sends a string.

§Two label shapes, and they are not the same shape

A storage carries "labels": [{key,value}]. A server carries "labels": {"label": [{key,value}]}. Both are filtered with ?label=key%3Dvalue. This is not a simplification anybody would invent; it is what the API does, and a client written against one shape breaks on the other.

§Behaviour 4: the list is not the detail

GET /1.3/server carries no storage_devices, no ip_addresses — those exist only in GET /1.3/server/{uuid}. GET /1.3/storage DOES carry labels. A caller that lists servers and reads attachments off the rows gets an empty list and concludes there is nothing attached.

§created IS sent — behaviour 10 as first written was wrong

MEASURED against the live account 2026-09-20, two volumes, both endpoints: GET /1.3/storage/private and GET /1.3/storage/{uuid} each carried "created". This crate previously omitted it from both and claimed the provider did too, which made a young volume and an old orphan the same row — a verdict path the real account cannot produce.

with_created is now true unless crate::Fault::WithholdCreatedField is armed by name. What behaviour 9 actually turns on is the TITLE, not the date: the Resize Backup carries no product name, and the label is what recognises it.

Constants§

PLANS
The plan table, ONCE. GET /1.3/plan renders this list and POST /1.3/server refuses anything not in it, so the endpoint can never advertise a plan the create would then call INVALID_PLAN.

Functions§

auth_failed
Behaviour 1 — the other error envelope, and the whole reason it is here.
cores_of
The plan table. Small on purpose: these are the plans this estate buys, and a plan name that is not here is refused by name rather than invented — an invented plan would price at zero and make a spend cap untestable.
error
The ORDINARY error envelope: {"error": {"error_code", "error_message"}}.
import
The direct-upload import object, exactly as the live one answers.
iso8601
Virtual milliseconds as an RFC 3339 stamp. The epoch is arbitrary and fixed (2026-01-01T00:00:00Z) so a replayed seed produces byte-identical stamps.
labels_enveloped
labels_flat
memory_of
not_implemented
The mock’s own refusal for a path it does not implement. Loud, and it names the path: an unimplemented call must never be able to read as a working one.
plan_known
price
GET /1.3/price.
server
One server row.
storage
One storage row. with_created is the fault; detail adds the servers envelope that only GET /1.3/storage/{uuid} carries.