Sausage spike: host (broker, supervisors, commit-on-close fs, cargo actor, console), agent actor, examples
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "crasher"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,6 @@
|
||||
//! A bad version: it starts, waits 3 seconds (long enough for the smoke
|
||||
//! test), then panics. Used to test the automatic rollback.
|
||||
fn main() {
|
||||
std::thread::sleep(std::time::Duration::from_secs(3));
|
||||
panic!("this version is broken");
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "echo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1"
|
||||
@@ -0,0 +1,15 @@
|
||||
use serde_json::{json, Value};
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
fn main() {
|
||||
let mut out = std::io::stdout().lock();
|
||||
for line in std::io::stdin().lock().lines() {
|
||||
let Ok(msg) = serde_json::from_str::<Value>(&line.unwrap()) else { continue };
|
||||
if msg["body"]["type"] == "shutdown" {
|
||||
break;
|
||||
}
|
||||
let env = json!({"to": msg["from"], "reply_to": msg["id"], "body": {"type": "echo", "got": msg["body"]}});
|
||||
writeln!(out, "{env}").unwrap();
|
||||
out.flush().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "fetch"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1"
|
||||
wstd = "0.6"
|
||||
@@ -0,0 +1,78 @@
|
||||
//! Fetches URLs over HTTP(S) and writes the body to /public/fetch/<n>.
|
||||
//! Reads stdin and runs requests at the same time: while a fetch runs,
|
||||
//! `ping` still answers at once.
|
||||
use serde_json::{Value, json};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wstd::http::{Body, Client, Request};
|
||||
use wstd::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
type Out = Rc<RefCell<Vec<String>>>;
|
||||
|
||||
async fn flush(out: &Out) {
|
||||
let lines: Vec<String> = out.borrow_mut().drain(..).collect();
|
||||
let mut stdout = wstd::io::stdout();
|
||||
for l in lines {
|
||||
let _ = stdout.write_all(format!("{l}\n").as_bytes()).await;
|
||||
}
|
||||
let _ = stdout.flush().await;
|
||||
}
|
||||
|
||||
async fn fetch(n: u64, msg: Value, out: Out) {
|
||||
let url = msg["body"]["url"].as_str().unwrap_or_default().to_string();
|
||||
let started = wstd::time::Instant::now();
|
||||
let result = async {
|
||||
let req = Request::get(&url).body(Body::empty()).map_err(|e| e.to_string())?;
|
||||
let resp = Client::new().send(req).await.map_err(|e| format!("{e:?}"))?;
|
||||
let status = resp.status().as_u16();
|
||||
let bytes = resp.into_body().contents().await.map_err(|e| format!("{e:?}"))?.to_vec();
|
||||
std::fs::create_dir_all("/public/fetch").map_err(|e| e.to_string())?;
|
||||
let path = format!("/public/fetch/{n}");
|
||||
std::fs::write(&path, &bytes).map_err(|e| e.to_string())?;
|
||||
Ok::<_, String>((status, bytes.len(), path))
|
||||
}
|
||||
.await;
|
||||
let ms = started.elapsed().as_millis();
|
||||
let body = match result {
|
||||
Ok((status, size, path)) => {
|
||||
let id = std::env::var("SAUSAGE_ACTOR").unwrap_or("fetch".into());
|
||||
json!({"type": "fetched", "status": status, "bytes": size, "path": path.replacen("/public", &format!("/actor/{id}/public"), 1), "ms": ms})
|
||||
}
|
||||
Err(e) => json!({"type": "error", "reason": e, "ms": ms}),
|
||||
};
|
||||
out.borrow_mut().push(json!({"to": msg["from"], "reply_to": msg["id"], "body": body}).to_string());
|
||||
flush(&out).await;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
wstd::runtime::block_on(async {
|
||||
let out: Out = Rc::new(RefCell::new(Vec::new()));
|
||||
let mut stdin = wstd::io::stdin();
|
||||
let mut buf = Vec::new();
|
||||
let mut chunk = vec![0u8; 8192];
|
||||
let mut n = 0u64;
|
||||
loop {
|
||||
let Ok(len) = stdin.read(&mut chunk).await else { break };
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
buf.extend_from_slice(&chunk[..len]);
|
||||
while let Some(pos) = buf.iter().position(|b| *b == b'\n') {
|
||||
let line: Vec<u8> = buf.drain(..=pos).collect();
|
||||
let Ok(msg) = serde_json::from_slice::<Value>(&line) else { continue };
|
||||
match msg["body"]["type"].as_str() {
|
||||
Some("shutdown") => return,
|
||||
Some("fetch") => {
|
||||
n += 1;
|
||||
wstd::runtime::spawn(fetch(n, msg, out.clone())).detach();
|
||||
}
|
||||
Some("ping") => {
|
||||
out.borrow_mut().push(json!({"to": msg["from"], "reply_to": msg["id"], "body": {"type": "pong"}}).to_string());
|
||||
flush(&out).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "notes"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1"
|
||||
@@ -0,0 +1,37 @@
|
||||
//! Keeps a list of notes in /data/notes.json and publishes them as
|
||||
//! /public/notes.txt.
|
||||
use serde_json::{Value, json};
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
const DATA: &str = "/data/notes.json";
|
||||
|
||||
fn save(notes: &[String]) {
|
||||
std::fs::write(DATA, serde_json::to_string(notes).unwrap()).unwrap();
|
||||
std::fs::write("/public/notes.txt", notes.join("\n") + "\n").unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut notes: Vec<String> = std::fs::read_to_string(DATA)
|
||||
.ok()
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.unwrap_or_default();
|
||||
let mut out = std::io::stdout().lock();
|
||||
for line in std::io::stdin().lock().lines() {
|
||||
let Ok(line) = line else { break };
|
||||
let Ok(msg) = serde_json::from_str::<Value>(&line) else { continue };
|
||||
let body = &msg["body"];
|
||||
let reply = match body["type"].as_str() {
|
||||
Some("shutdown") => break,
|
||||
Some("add") => {
|
||||
notes.push(body["text"].as_str().unwrap_or_default().to_string());
|
||||
save(¬es);
|
||||
json!({"type": "added", "count": notes.len()})
|
||||
}
|
||||
Some("list") => json!({"type": "notes", "items": notes}),
|
||||
Some("crash") => panic!("asked to crash"),
|
||||
_ => json!({"type": "error", "reason": "unknown message"}),
|
||||
};
|
||||
writeln!(out, "{}", json!({"to": msg["from"], "reply_to": msg["id"], "body": reply})).unwrap();
|
||||
out.flush().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"send": {"type": "add", "text": "test note"}}
|
||||
{"expect": {"type": "added"}}
|
||||
{"send": {"type": "list"}}
|
||||
{"expect": {"type": "notes"}}
|
||||
Reference in New Issue
Block a user