Skip to main content

Module cloud_vps

Module cloud_vps 

Source
Expand description

cloud.vps.* verb signatures — the spike catalog (R409-T3).

Per W144 §“What this doc is not deciding” and R409-T11 (catalog-shape postmortem), the broader cloud.* / dns.* / etc. surface is gated on validating this shape against a second native provider (R409-T10: DigitalOcean). Only cloud.vps.create, cloud.vps.destroy, and cloud.vps.status land here — the three verbs W144 explicitly calls out as “already partially present in MachineProvider.”

Wire types are plain serde structs (with optional schemars::JsonSchema under the json-schema feature). They are deliberately not the same as the in-crate domain types (crate::provider::ServerSpec, crate::provider::ServerStatus) — the adapter is the boundary that converts between them. Keeping wire and domain types separate is what lets the same verb shape serve Hetzner, DigitalOcean, and (eventually) a synthetic LocalDocker adapter without leaking vendor-specific fields.

@yah:ticket(R594-F5, “floating_ip.* envoy verb: raft floating-ingress ownership commands the provider IP to follow placement”) @yah:status(review) @yah:assignee(agent:claude) @yah:at(2026-07-02T19:56:32Z) @yah:phase(P4) @yah:parent(R594) @yah:next(“New envoy verb family alongside dns.* — assign/reassign a provider floating IP (Hetzner floating IP / OVH Additional IP / Vultr reserved IP) to the node that raft says owns floating ingress (raft/mod.rs:8 SetIngressOwner already exists). Provider mobility constraints verified 2026-07 and folded into W267 §Tier 1: Hetzner within network zone, OVH within DC/region, Vultr region-bound. This is the sovereign-tier analog of the external-identity-follows-placement property R591 names for Headscale/CF-Tunnel — LINKED behind R591 (live-peer-owned, never claim) so the two follow-placement mechanisms land coherently, and behind R572-F3 (public-ip taint lives on machine TOML).”) @yah:verify(“cargo test -p yah-cloud envoy::floating_ip; fixture: ownership flip drives exactly one reassign call; idempotent re-apply”) @yah:tier(Warrior) @yah:handoff(“Landed the floating_ip.* envoy verb family + Hetzner/OVH/Vultr provider adapters, code+mock-tests only (no live calls, per constraint).\n\nHOMED IN: oss/yubaba/crates/cloud/src/envoy/floating_ip.rs (new module, mirrors dns_record.rs’s wire-signature-only shape: FloatingIpAssign/FloatingIpStatus marker types, Input/Output structs, InternalVerb impls). Added VerbCategory::FloatingIp ("floating_ip" namespace) to envoy.rs, registered in known_verb_descriptors() (9->11), and fixed VerbCategory’s #[serde(rename_all)] from "lowercase" to "snake_case" (a pre-existing latent bug that only bit once a multi-word variant existed: "FloatingIp" lowercased to "floatingip" instead of "floating_ip" — no-op for the six existing single-word variants).\n\nPROVIDER ABSTRACTION: oss/yubaba/crates/cloud/src/provider/floating_ip.rs defines FloatingIpProvider trait (resolve_target/current_assignment/reassign) + reconcile_assignment (the shared idempotent+zone-checked core) + on_ingress_owner_changed (the Rust-level callable entry point, doc-commented with exactly where leader.rs should call it — not touched, peer-owned). Three adapters, one file each, same shape as hetzner_envoy.rs/digitalocean.rs (thin reqwest client, with_base_url override, EnvoyAdapter + FloatingIpProvider impls): hetzner_floating_ip.rs, ovh_floating_ip.rs (new — no prior OVH code existed anywhere in-tree; auth is a placeholder header, flagged for real OVH request-signing before live use), vultr_floating_ip.rs.\n\nMACHINE RESOLUTION: reads existing MachineConfig.location/.region only (no new fields). Hetzner: location (DC code) -> network zone via a small table (hil/ash/fsn1-nbg1-hel1/sin). OVH: location if set else region (today’s OVH-labeled machines are provider="static" with region set but no location — R572-F3’s problem, not touched). Vultr: location directly (region-bound, no mapping needed) with a sanity cross-check against the live instance’s own region.\n\nIDEMPOTENCY: reconcile_assignment fetches current_assignment first; attached_to == target.attach_id -> reassigned:false, zero network calls to the reassign endpoint. Zone mismatch bails before any reassign call. Verified in 3 independent test layers: pure fake-provider unit tests (provider/floating_ip.rs), and per-provider axum in-process mock tests (127.0.0.1:0, stateful Arc+ AtomicU32 call counters — same convention as reconciler/pond.rs and reconciler/static_asset.rs) for Hetzner/OVH/Vultr.\n\nWIRING (deferred, flagged in code): on_ingress_owner_changed’s doc comment names the exact call site (leader.rs’s raft-reconcile path, on ingress_owner transitioning to Some(machine)) and notes "which IP is the ingress IP" needs a small config surface a follow-up should add alongside the wiring, not invented here.\n\nVERIFY: cargo test -p cloud –lib floating_ip -> 27 passed, 0 failed. cargo test -p cloud –lib (full) -> 503 passed, 0 failed, 4 ignored. cargo check -p cloud clean. cargo check -p yubaba clean. Pre-existing unrelated break: cargo test -p cloud (all targets) fails to compile tests/pond_smoke.rs (missing git field on ServiceComponent literal) — not touched by this ticket, not caused by this change (file has zero diff from HEAD), looks like another peer’s in-flight WIP on the shared tree.\n\nLive-provider verification (real credentials, real reassign against Hetzner/OVH/Vultr) is explicitly deferred to the operator, same as every other cloud envoy adapter.\n\nFiles: oss/yubaba/crates/cloud/src/envoy.rs, oss/yubaba/crates/cloud/src/envoy/floating_ip.rs (new), oss/yubaba/crates/cloud/src/provider/mod.rs, oss/yubaba/crates/cloud/src/provider/floating_ip.rs (new), oss/yubaba/crates/cloud/src/provider/hetzner_floating_ip.rs (new), oss/yubaba/crates/cloud/src/provider/ovh_floating_ip.rs (new), oss/yubaba/crates/cloud/src/provider/vultr_floating_ip.rs (new), oss/yubaba/crates/cloud/src/lib.rs.”)

Structs§

CloudVpsCreate
Marker type for the cloud.vps.create verb.
CloudVpsCreateInput
Request body for cloud.vps.create.
CloudVpsCreateOutput
Response body for cloud.vps.create.
CloudVpsDestroy
Marker type for the cloud.vps.destroy verb.
CloudVpsDestroyInput
Request body for cloud.vps.destroy.
CloudVpsDestroyOutput
Response body for cloud.vps.destroy. Empty — adapters return Ok({}) whether the server existed or was already gone (idempotent).
CloudVpsStatus
Marker type for the cloud.vps.status verb.
CloudVpsStatusInput
Request body for cloud.vps.status.
CloudVpsStatusOutput
Response body for cloud.vps.status.

Enums§

VpsPhase
Canonical VPS lifecycle phase. Maps onto crate::provider::ServerStatus without the Unknown(String) payload — the free-form vendor detail rides in CloudVpsStatusOutput::detail instead so the wire shape stays a closed enum that downstream UI can render directly.

Functions§

server_status_to_output
Pure conversion: domain crate::provider::ServerStatus → wire CloudVpsStatusOutput.