Skip to main content

Crate upcloud_api

Crate upcloud_api 

Source
Expand description

upcloud-api — the UpCloud API 1.3 surface as ONE trait, ONE wire, ONE decision about which cloud answers.

§Why this crate exists

On 2026-09-21 the nordisk estates had five UpCloud clients: private-gunnar-ops’s xtask (two of them, until lane T3 made them one), gunnar/deploy/upcloud (the re-image procedure, with its OWN scripted fake), gunnar-loadbench, private-holger-ops’s xtask, and monetize-cloud-impl. Each one spelled https://api.upcloud.com/1.3 for itself. Each one decided for itself whether a run was aimed at a fake. Four bugs that day were the same bug: a mock run reached the account because one client in the chain decided differently from the rest — the worst of them was gunnar-upcloud’s Api::new(DEFAULT_BASE), so cargo xtask mock reimage --apply would have re-imaged the LIVE appliance with the estate’s token while the banner said FAKE.

A test against the fake is only worth something if the code under test is the SAME code that runs against the account. So:

  • UpCloudApi is the only surface. No method takes a path, a query string or a base URL, so no caller can name an endpoint.
  • There is one wire implementation. It builds every request for the account and for a mock the same way, from the same code — only the base differs. A run against mock-upcloud therefore exercises the exact URL, body and header construction a run against the account does.
  • The base comes from an Endpoint, and an Endpoint is decided ONCE, at the edge of the program, from what the operator TYPED (Endpoint::account / Endpoint::mock). A mock endpoint is loopback-only by construction; the account endpoint refuses to exist while the shell carries a mock variable (MOCK_ENVS).
  • The in-process fake — FakeUpCloud over mock-upcloud’s Estate — lives beside that state machine in the mock-upcloud crate, so a fault armed once applies to terraform (the HTTP face) and to Rust callers (the trait face) alike: one world, two faces.
  • over is where a typed call becomes ONE method, path and body: the wire and every fake are an Exchange under Over, so a fake answers the exact request the account would have been sent.
  • guard is the test every consuming repository runs: nothing outside the files it names may spell the provider or build an API path.

§The method set is DERIVED, not designed

Every method exists because a real call site in one of the ported clients calls that endpoint. The doc on each names the caller. A general-purpose UpCloud client is a second thing to keep true; this is not one.

Re-exports§

pub use over::Call;
pub use over::Exchange;
pub use over::Method;
pub use over::Over;

Modules§

body
The JSON each write sends. Public so an in-process fake reads the SAME bodies the wire sends rather than a parallel copy of UpCloud’s quirks.
guard
The test every consuming repository runs: nothing outside the files it names spells the provider or builds an UpCloud API path.
net
The transport boundary: what is retried and what never is.
over
One spelling of UpCloud’s paths, under every implementation.

Structs§

MockBase
A loopback base ending /1.3. The field is private, so the only way to hold one is Endpoint::mock — which is what makes “a fake pointed at a real host” unrepresentable rather than merely refused.
NewStorage
A new storage.
Options
Timeouts. A single API call and the upload of a multi-hundred-MiB medium cannot share one budget without one of them being wrong.
Reply
One answer from the API, with NO judgement about its status. The callers decide what a code means, because they genuinely disagree: a 404 on GET /server/{uuid} is a FACT for a sweep and a REFUSAL for a resize. A transport failure is the Err and is never a status.

Enums§

Backups
What a storage’s delete does with the storage’s backups.
BootOrder
What the hypervisor boots first on its next START (a guest reboot does not re-read it — MEASURED 2026-09-14).
Console
The VNC console. Vnc re-provisions host AND port; the reply carries them.
Credential
How the wire authenticates. The account’s clients use a token; monetize’s plugin can also be configured with a sub-account’s username and password.
DeviceKind
How a device rides on a server.
Endpoint
Which UpCloud answers. Built only by Endpoint::account (refuses in a mock-carrying shell) or Endpoint::mock (refuses anything off loopback).
Stop
How a server is stopped. Soft carries a grace in seconds; Hard pulls the plug, and is the word a delete needs first.
WithStorages
Whether a server’s delete takes its storages with it, and what becomes of their backups. UpCloud spells this as a query string; no caller spells it.

Constants§

ACCOUNT_BASE_FOR_DISPLAY
The account’s base, for a REPORT and a display comparison — never to build a request from; nothing can be called on a &'static str.
MOCK_API_FLAG
The flag Endpoint::child_args emits and Endpoint::from_flag reads.
MOCK_BASE_ENV
The variable a mock-selected run reads its loopback base from. On an account-selected run its PRESENCE is a refusal (Endpoint::account).
MOCK_ENVS
Every variable whose presence means “this shell is aimed at a fake”.
TF_MOCK_BASE_ENV
The terraform provider’s own debug knob. Not read here — named so an account run can refuse when the shell carries it.

Traits§

UpCloudApi
The UpCloud API 1.3 surface the nordisk estates actually use. Typed in, typed out. Not one method takes a path, a query string or a base URL.

Functions§

connect
The only constructor. The implementation a run holds is a function of the Endpoint, and an Endpoint is only ever built from what the operator typed.
delete_server_query
The query a delete sends — ONE spelling, for the wire and for any fake that records what a call means.
delete_storage_query
See delete_server_query.
is_loopback
http://127.0.0.1:PORT or http://localhost:PORT, and nothing else. No IPv6 — these estates have none by law, and [::1] is refused with the rest.
label_query
?label=k%3Dv&label=…, or "" — every character outside RFC 3986’s unreserved set percent-encoded, so a label is never read as query syntax.
redact_upload_url
https://fi-hel1.img.upcloud.com/uploader/session/<secret>…/uploader/session/…: the session id IS a credential (anyone holding it can write the storage a box boots from), so it is never printed whole.

Type Aliases§

Label
One label, key=value. UpCloud filters lists by it (?label=key%3Dvalue) and carries it on storages and servers.