starfield-datastore 0.1.3

Pull-through artifact cache with fail-closed validation, an S3 mirror served by presigned redirect, and a credential model that keeps upstream secrets in one place
Documentation
#![cfg(feature = "cli")]
use std::{
    path::Path,
    process::{Command, Output},
};
#[allow(dead_code)]
mod support;

fn run(root: &Path, args: &[&str]) -> Output {
    Command::new(env!("CARGO_BIN_EXE_starfield-datastore"))
        .env_clear()
        .env("STARFIELD_CACHE_DIR", root.join("cache"))
        .env("STARFIELD_DATASTORE_CONFIG", root.join("config.toml"))
        .args(args)
        .output()
        .unwrap()
}
fn success(output: Output) -> String {
    assert!(
        output.status.success(),
        "{}",
        String::from_utf8_lossy(&output.stderr)
    );
    String::from_utf8(output.stdout).unwrap()
}

#[test]
fn one_config_selects_profiles_and_resolves_only_their_credentials() {
    let root = tempfile::tempdir().unwrap();
    let stub = support::stub::Stub::start("127.0.0.1");
    stub.route("/one", support::stub::Response::ok(vec![7; 2048]));
    stub.route("/two", support::stub::Response::ok(vec![8; 2048]));
    std::fs::write(root.path().join("artifacts.toml"), format!(
        "[[artifact]]\nkey='one'\nsources=['{}/one']\n[[artifact]]\nkey='two'\nsources=['{}/two']\n", stub.url(), stub.url()
    )).unwrap();
    let config = root.path().join("services.toml");
    std::fs::write(
        &config,
        r#"
allow_upstream = true
[[credentials]]
name = "first"
hosts = ["127.0.0.1"]
type = "env"
variable = "SFD_FIRST_TOKEN"
[[credentials]]
name = "second"
hosts = ["127.0.0.1"]
type = "env"
variable = "SFD_SECOND_TOKEN"
[[credentials]]
name = "future"
hosts = ["127.0.0.1"]
type = "future-auth"
opaque = "must-not-be-logged"
[services.one]
manifest = "artifacts.toml"
credentials = ["first"]
[services.two]
manifest = "artifacts.toml"
credentials = ["second"]
[services.preview]
manifest = "artifacts.toml"
credentials = ["future"]
"#,
    )
    .unwrap();
    for (service, variable, token) in [
        ("one", "SFD_FIRST_TOKEN", "first"),
        ("two", "SFD_SECOND_TOKEN", "second"),
    ] {
        success(
            Command::new(env!("CARGO_BIN_EXE_starfield-datastore"))
                .env_clear()
                .env("STARFIELD_CACHE_DIR", root.path().join("cache"))
                .env(variable, token)
                .args([
                    "--config",
                    config.to_str().unwrap(),
                    "--service",
                    service,
                    "fetch",
                    "--key",
                    service,
                ])
                .output()
                .unwrap(),
        );
    }
    let requests = stub.requests();
    assert_eq!(
        requests[0].headers.get("authorization").map(String::as_str),
        Some("Bearer first")
    );
    assert_eq!(
        requests[1].headers.get("authorization").map(String::as_str),
        Some("Bearer second")
    );
    let skipped = run(
        root.path(),
        &[
            "--config",
            config.to_str().unwrap(),
            "--service",
            "preview",
            "list",
        ],
    );
    assert!(skipped.status.success());
    let warning = String::from_utf8(skipped.stderr).unwrap();
    assert!(warning.contains("future-auth") && warning.contains("skipped"));
    assert!(!warning.contains("must-not-be-logged"));
    let flags_win = run(
        root.path(),
        &[
            "--config",
            config.to_str().unwrap(),
            "--service",
            "preview",
            "fetch",
            "--manifest",
            "missing-override.toml",
            "--key",
            "one",
        ],
    );
    assert!(
        !flags_win.status.success(),
        "explicit manifest flag overrides the profile"
    );
}

#[test]
fn commands_import_fetch_verify_list_and_gc_without_network() {
    let root = tempfile::tempdir().unwrap();
    let manifest = root.path().join("manifest.toml");
    std::fs::write(
        &manifest,
        "[[artifact]]\nkey='manual/data'\nsources=[]\nbytes=4\ncheck={none=true}\n",
    )
    .unwrap();
    let input = root.path().join("data");
    std::fs::write(&input, b"data").unwrap();
    let manifest = manifest.to_str().unwrap();
    let input = input.to_str().unwrap();
    let imported = success(run(
        root.path(),
        &[
            "import",
            "--manifest",
            manifest,
            "--key",
            "manual/data",
            "--from",
            input,
        ],
    ));
    let fetched = success(run(
        root.path(),
        &["fetch", "--manifest", manifest, "--key", "manual/data"],
    ));
    assert_eq!(imported, fetched);
    assert_eq!(
        success(run(root.path(), &["list", "--bytes"])),
        "manual/data\t4\n"
    );
    assert_eq!(
        success(run(root.path(), &["verify", "--manifest", manifest])),
        "ok manual/data\n"
    );
    std::fs::write(imported.trim(), b"evil").unwrap();
    let failed = run(root.path(), &["verify", "--manifest", manifest]);
    assert!(!failed.status.success());
    assert!(String::from_utf8_lossy(&failed.stderr).contains("digest"));
    assert_eq!(std::fs::read(imported.trim()).unwrap(), b"evil");
    assert_eq!(
        success(run(root.path(), &["gc", "--max-bytes", "0"])),
        "manual/data\n"
    );
    assert_eq!(success(run(root.path(), &["list"])), "");
    success(run(root.path(), &["remove", "--key", "manual/data"]));
}

#[cfg(all(unix, feature = "server"))]
#[test]
fn service_handles_sigterm_cleanly_without_contacting_aws() {
    use std::{
        process::Stdio,
        time::{Duration, Instant},
    };
    let root = tempfile::tempdir().unwrap();
    let manifest = root.path().join("empty.toml");
    std::fs::write(&manifest, "").unwrap();
    let reservation = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
    let address = reservation.local_addr().unwrap();
    drop(reservation);
    let mut child = Command::new(env!("CARGO_BIN_EXE_starfield-datastore"))
        .env_clear()
        .env("STARFIELD_CACHE_DIR", root.path().join("cache"))
        .env(
            "STARFIELD_DATASTORE_CONFIG",
            root.path().join("config.toml"),
        )
        .env("AWS_ACCESS_KEY_ID", "test-access")
        .env("AWS_SECRET_ACCESS_KEY", "test-secret")
        .env("AWS_EC2_METADATA_DISABLED", "true")
        .args([
            "serve",
            "--manifest",
            manifest.to_str().unwrap(),
            "--bucket",
            "s3://unused-test-bucket/prefix",
            "--region",
            "us-east-1",
            "--bind",
            &address.to_string(),
        ])
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap();
    let client = reqwest::blocking::Client::builder()
        .no_proxy()
        .timeout(Duration::from_millis(200))
        .build()
        .unwrap();
    let deadline = Instant::now() + Duration::from_secs(10);
    loop {
        if client
            .get(format!("http://{address}/healthz"))
            .send()
            .is_ok_and(|r| r.status().is_success())
        {
            break;
        }
        if Instant::now() > deadline || child.try_wait().unwrap().is_some() {
            let _ = child.kill();
            let output = child.wait_with_output().unwrap();
            panic!(
                "service failed to start: {}",
                String::from_utf8_lossy(&output.stderr)
            );
        }
        std::thread::sleep(Duration::from_millis(20));
    }
    assert!(Command::new("kill")
        .args(["-TERM", &child.id().to_string()])
        .status()
        .unwrap()
        .success());
    let deadline = Instant::now() + Duration::from_secs(5);
    loop {
        if let Some(status) = child.try_wait().unwrap() {
            assert!(status.success(), "SIGTERM was not handled gracefully");
            break;
        }
        if Instant::now() > deadline {
            let _ = child.kill();
            let _ = child.wait();
            panic!("service did not stop after SIGTERM");
        }
        std::thread::sleep(Duration::from_millis(20));
    }
}

#[test]
fn missing_manual_artifact_explains_how_to_obtain_it() {
    let root = tempfile::tempdir().unwrap();
    let manifest = root.path().join("manifest.toml");
    std::fs::write(&manifest,"[[artifact]]\nkey='manual/library'\nsources=[]\ndescription='Request the library, then import the emailed archive'\n").unwrap();
    let failed = run(
        root.path(),
        &[
            "fetch",
            "--manifest",
            manifest.to_str().unwrap(),
            "--key",
            "manual/library",
        ],
    );
    assert!(!failed.status.success());
    assert!(String::from_utf8_lossy(&failed.stderr).contains("emailed archive"));
}

#[test]
fn explicit_repair_reports_shared_aliases_then_restores_pinned_content() {
    use sha2::{Digest, Sha256};
    let root = tempfile::tempdir().unwrap();
    let stub = support::stub::Stub::start("127.0.0.1");
    stub.route("/data", support::stub::Response::ok(b"data".to_vec()));
    std::fs::write(root.path().join("config.toml"), "allow_upstream=true").unwrap();
    let manifest = root.path().join("manifest.toml");
    let entry = format!(
        "[[artifact]]\nkey='data'\nsources=['{}/data']\nsha256='{:x}'\ncheck={{none=true}}\n",
        stub.url(),
        Sha256::digest(b"data")
    );
    std::fs::write(
        &manifest,
        format!("{entry}[[artifact]]\nkey='alias'\ncheck={{none=true}}\n"),
    )
    .unwrap();
    let input = root.path().join("input");
    std::fs::write(&input, b"data").unwrap();
    let path = success(run(
        root.path(),
        &[
            "import",
            "--manifest",
            manifest.to_str().unwrap(),
            "--key",
            "data",
            "--from",
            input.to_str().unwrap(),
        ],
    ));
    success(run(
        root.path(),
        &[
            "import",
            "--manifest",
            manifest.to_str().unwrap(),
            "--key",
            "alias",
            "--from",
            input.to_str().unwrap(),
        ],
    ));
    std::fs::write(&manifest, entry).unwrap();
    std::fs::write(path.trim(), b"evil").unwrap();
    let failed = run(
        root.path(),
        &[
            "verify",
            "--manifest",
            manifest.to_str().unwrap(),
            "--repair",
        ],
    );
    assert!(!failed.status.success());
    assert!(String::from_utf8_lossy(&failed.stderr).contains("alias"));
    assert!(stub.requests().is_empty());
    success(run(root.path(), &["remove", "--key", "alias"]));
    success(run(
        root.path(),
        &[
            "verify",
            "--manifest",
            manifest.to_str().unwrap(),
            "--repair",
        ],
    ));
    assert_eq!(std::fs::read(path.trim()).unwrap(), b"data");
    assert_eq!(stub.requests().len(), 1);
}