Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/mock-scaleway/src/bin/mock-scaleway.rs

//! `mock-scaleway` — run the fake Scaleway Instance API.
//!
//!     mock-scaleway --port 8098                       # faithful timings (poweron 30 s)
//!     mock-scaleway --port 8098 --speed 0             # virtual time: each request moves it 5 s
//!     mock-scaleway --port 8098 --arm out-of-stock    # behaviour 3, sticky
//!     mock-scaleway --port 8098 --token scw-…          # the secret key it accepts (repeatable)
//!     mock-scaleway --port 8098 --engine container --bin-dir <dir> --by-hostname PREFIX=MEDIUM.json
//!
//! `--speed` is in thousandths (`1000` = real time), as mock-upcloud's.

use std::sync::Arc;

use mock_scaleway::{Fault, Mock};
use mock_upcloud::container::{ContainerEngine, Medium, Settings};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut port: u16 = 8098;
    let mut speed: u64 = 1000;
    let mut listen = "127.0.0.1".to_string();
    let mut tokens: Vec<String> = Vec::new();
    let mut arm: Vec<Fault> = Vec::new();
    let mut engine: Option<String> = None;
    let mut loopback: Option<(u8, u8)> = None;
    let mut cs = Settings {
        root: std::path::PathBuf::from("/tmp/mock-scaleway-container"),
        network: String::new(),
        image: "docker.io/library/alpine:3".into(),
        bin_dir: std::path::PathBuf::new(),
        by_hostname: Vec::new(),
        label: String::new(),
        engine: "podman".into(),
        utility: mock_upcloud::container::MOCK_UTILITY.into(),
    };
    let mut args = std::env::args().skip(1);
    while let Some(a) = args.next() {
        let mut val = |what: &str| args.next().ok_or_else(|| format!("{a} wants {what}"));
        match a.as_str() {
            "--port" => port = val("a number")?.parse()?,
            "--speed" => speed = val("a number")?.parse()?,
            "--listen" => listen = val("an address")?,
            "--token" => tokens.push(val("a secret key")?),
            "--arm" => {
                let n = val("a fault name")?;
                let (name, ct) = n
                    .split_once(':')
                    .map(|(a, b)| (a.to_string(), Some(b.to_string())))
                    .unwrap_or((n.clone(), None));
                arm.push(Fault::parse(&name, ct.as_deref()).ok_or_else(|| {
                    format!("no such fault: {n} (the faults: out-of-stock[:<commercial_type>])")
                })?);
            }
            "--public-on-loopback" => {
                let v = val("A.B")?;
                let (x, y) = v.split_once('.').ok_or("--public-on-loopback wants A.B")?;
                loopback = Some((x.parse()?, y.parse()?));
            }
            "--engine" => engine = Some(val("`container`")?),
            "--root" => cs.root = val("a directory")?.into(),
            "--network" => cs.network = val("a name")?,
            "--image" => cs.image = val("an image")?,
            "--bin-dir" => cs.bin_dir = val("a directory")?.into(),
            "--label" => cs.label = val("a word")?,
            "--utility" => cs.utility = val("X.Y")?,
            "--by-hostname" => {
                let v = val("PREFIX=MEDIUM.json")?;
                let (prefix, file) = v
                    .split_once('=')
                    .ok_or("--by-hostname wants PREFIX=MEDIUM.json")?;
                let m = Medium::parse(&std::fs::read(file).map_err(|e| format!("{file}: {e}"))?)?
                    .ok_or_else(|| format!("{file} is not a container medium"))?;
                cs.by_hostname.push((prefix.to_string(), m));
            }
            "--help" | "-h" => {
                println!("mock-scaleway [--port N] [--speed MILLI] [--listen ADDR] [--token KEY]… [--arm out-of-stock[:TYPE]]… [--public-on-loopback A.B] [--engine container --bin-dir DIR --by-hostname PREFIX=MEDIUM.json …]");
                return Ok(());
            }
            other => return Err(format!("unknown argument {other}").into()),
        }
    }
    let mock = Mock::new(mock_scaleway::Clock::new(speed));
    let mut container: Option<Arc<ContainerEngine>> = None;
    if let Some(e) = engine.as_deref() {
        if e != "container" {
            return Err(format!("--engine {e}: the engine is `container`").into());
        }
        if cs.label.is_empty() {
            cs.label = format!("msw{}", std::process::id());
        }
        if cs.network.is_empty() {
            cs.network = format!("mock-scaleway-{}", cs.label);
        }
        let ce = Arc::new(ContainerEngine::new(cs.clone()));
        ce.ensure_network()?;
        container = Some(ce);
    }
    mock.with(|a| {
        if !tokens.is_empty() {
            a.tokens = tokens.clone();
        }
        a.faults = arm.clone();
        a.public_on_loopback = loopback.or(container.as_ref().map(|_| (81, 0)));
        a.utility = cs.utility.clone();
        if let Some(ce) = &container {
            a.engine = Some(ce.clone() as Arc<dyn mock_upcloud::kvm::GuestEngine>);
        }
    });
    let p = mock.serve_on(&format!("{listen}:{port}"))?;
    println!(
        "mock-scaleway on http://{listen}:{p} (speed {speed}/1000, key {})",
        if tokens.is_empty() {
            mock_scaleway::TOKEN.to_string()
        } else {
            format!("{} given", tokens.len())
        }
    );
    loop {
        std::thread::sleep(std::time::Duration::from_secs(3600));
    }
}