remote.v

2.0 KB · raw

1//! The remote interface: five operations, two backends.2//!3//! A remote is dumb storage that can do one safe swap. Everything clever —4//! what to upload, what diverged, how to combine — lives in `src/exchange`, above5//! this interface, so the folder and S3 backends stay thin and the sync logic6//! is written once and tested offline.78module remote910// get not finding a key is a normal answer, not an error, so `Object.found`11// carries it and errors mean real trouble (a refused connection, bad keys).12pub struct Object {13pub:14	found bool15	body  []u816	tag   string17}1819// Backend is one place to put bytes. `put_if_absent` never overwrites and20// `put_if_match` only overwrites when the version still matches, which is what21// makes two machines offering at once safe.22pub interface Backend {23mut:24	get(key string) !Object25	list(prefix string) ![]string26	put_if_absent(key string, body []u8) !bool27	put_if_match(key string, body []u8, tag string) !bool28	delete(key string) !29}3031pub const probe_key = 'koh-probe'3233// check runs the connection test `koh remote` insists on before saving a34// setting: write a probe object, confirm a second write of it is refused, then35// delete it. A backend that cannot do that cannot keep two machines honest.36pub fn check(mut backend Backend) ! {37	payload := 'koh probe'.bytes()38	// The probe is Koh's own key; one left by an interrupted check would make39	// the first write below look refused.40	backend.delete(probe_key)!41	if !backend.put_if_absent(probe_key, payload)! {42		return error('the remote refused to store a new object')43	}44	defer {45		// Also on the early returns below; deleting twice is harmless.46		backend.delete(probe_key) or {}47	}48	if backend.put_if_absent(probe_key, payload)! {49		return error('the remote overwrote an existing object — conditional writes are not working')50	}51	probe := backend.get(probe_key)!52	if !probe.found || probe.body != payload {53		return error('the remote did not give back what was written')54	}55	// Strip needs delete, so a remote that cannot delete fails here.56	backend.delete(probe_key)!57}