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

nordisk/holger

holger-estate-front/tests/holger_cli_reaches_the_estate_through_the_front.rs

//! **`holger-cli --at https://<repo host>` reaches the estate through the FRONT** (holger
//! lane B3, owner 2026-10-26; the pattern of Gunnar's A6).
//!
//! vm1's :443 (edda `estate-front::web`, the certificate the installer FED) carries a gRPC
//! call to the repository host (`repo_hosts`) to the PRIMARY's control plane on its utility
//! address, with no logic of its own: TLS ends at the front, the call goes on as h2c, the
//! trailers come back, rate-limited per caller (`ROUTE_GRPC_BURST`). Here the primary is a
//! REAL holger in this test process, and the client is the real `holger-cli`, dialling the
//! front by `--connect` while the name it asks for stays the repository host's.

use std::collections::BTreeMap;
use std::io::Write as _;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use nordisk_pairing::join::{Assigned, JoinReply, Peer};
use nordisk_pairing::Role;

const CONSOLE: &str = "c0nsole-bearer-0123456789abcdefghij";

fn reply(feed: &BTreeMap<String, String>) -> JoinReply {
    let peer = |name: &str, role, last: u8| Peer {
        name: name.into(),
        role,
        tunnel_pubkey: String::new(),
        overlay: nordisk_pairing::OVERLAY_BOX_A,
        endpoint: None,
        utility: Some([127, 0, 0, last].into()),
    };
    JoinReply {
        estate: "holger.test".into(),
        me: Assigned {
            name: "vm1".into(),
            overlay: nordisk_pairing::OVERLAY_FRONT,
        },
        cert_pem: None,
        role: Some(Role::Front),
        primary_overlay: None,
        primary_utility: None,
        peers: vec![peer("vm2", Role::Primary, 1), peer("vm3", Role::Twin, 3)],
        feed: feed.clone(),
    }
}

/// A real holger on 127.0.0.1 (the primary's utility address): one raw repository, the
/// console's static bearer as Admin. Answers `(its data dir, the server, its gRPC port)`.
fn primary() -> (tempfile::TempDir, holger_server_lib::Holger, u16) {
    for _ in 0..8 {
        let dir = tempfile::tempdir().unwrap();
        let d = dir.path().display().to_string();
        let free = || {
            std::net::TcpListener::bind("127.0.0.1:0")
                .unwrap()
                .local_addr()
                .unwrap()
                .port()
        };
        let (grpc, http) = (free(), free());
        let ron = format!(
            r#"(
    repositories: [ (ron_name: "files", ron_repo_type: "raw", ron_upstreams: [], ron_in: None,
        ron_out: Some((ron_storage_endpoint: "storage", ron_exposed_endpoint: "door")), ron_data_dir: Some("{d}/files")) ],
    exposed_endpoints: [ (ron_name: "door", ron_url: "127.0.0.1:{grpc}", ron_http_url: Some("127.0.0.1:{http}")) ],
    storage_endpoints: [ (ron_name: "storage", ron_storage_type: "znippy", ron_path: "{d}") ],
    audit: (dir: "{d}/audit", enabled: true),
    auth: Some(( methods: [ StaticToken(subject: "console", token: Some("{CONSOLE}")) ], roles: {{"console": Admin}} )),
)"#
        );
        let cfg = dir.path().join("holger.ron");
        std::fs::File::create(&cfg)
            .unwrap()
            .write_all(ron.as_bytes())
            .unwrap();
        let started = (|| -> Result<holger_server_lib::Holger, String> {
            let mut h = holger_server_lib::read_ron_config(&cfg).map_err(|e| e.to_string())?;
            h.instantiate_backends().map_err(|e| e.to_string())?;
            holger_server_lib::wire_holger(&mut h).map_err(|e| e.to_string())?;
            h.start().map_err(|e| e.to_string())?;
            Ok(h)
        })();
        if let Ok(h) = started {
            return (dir, h, grpc);
        }
    }
    panic!("the primary never started");
}

#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn holger_cli_reaches_the_estate_through_the_fronts_repository_route() {
    let _ = rustls::crypto::ring::default_provider().install_default();
    let (pdir, _holger, grpc) = primary();

    let dir = tempfile::tempdir().unwrap();
    let (chain, key) =
        estate_front::web::certs::self_signed(&["holger.test", "repo.holger.test"], 80);
    let ca = dir.path().join("estate-ca.pem");
    std::fs::write(&ca, &chain).unwrap();
    let token = dir.path().join("console-token");
    std::fs::write(&token, CONSOLE).unwrap();
    let feed = BTreeMap::from([
        (
            "web_hosts".to_string(),
            "holger.test,repo.holger.test".to_string(),
        ),
        ("repo_hosts".to_string(), "repo.holger.test".to_string()),
        ("repo_grpc_port".to_string(), grpc.to_string()),
        (
            estate_front::web::certs::fed_cert_key("holger.test"),
            chain.clone(),
        ),
        (estate_front::web::certs::fed_key_key("holger.test"), key),
    ]);
    let latest: Arc<Mutex<JoinReply>> = Arc::new(Mutex::new(reply(&feed)));
    let l80 = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let l443 = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let a443 = l443.local_addr().unwrap();
    let f = latest.clone();
    let web = estate_front::web::start_on(
        dir.path(),
        "holger.test".into(),
        Arc::new(move || f.lock().unwrap().feed.clone()),
        l80,
        l443,
    );
    let r = latest.clone();
    estate_front::route::start(
        web.app.clone(),
        Arc::new(move || {
            let reply = r.lock().unwrap().clone();
            Some((
                reply.feed.clone(),
                nordisk_pairing::frontside::primary_utility(&reply),
            ))
        }),
        "holger.test".into(),
        holger_estate_front::PRODUCT,
        Some((
            holger_estate_front::HTTP_PORT,
            holger_estate_front::CONTROL_PORT,
        )),
        holger_estate_front::CARRIED_GRPC,
    );

    let at = format!("https://repo.holger.test:{}", a443.port());
    let connect = a443.to_string();
    let home = dir.path().join("cli-home").display().to_string();
    let cli = |who: Option<&str>, args: &[&str]| {
        let mut argv: Vec<String> = [
            "holger-cli",
            "--at",
            &at,
            "--connect",
            &connect,
            "--ca",
            &ca.display().to_string(),
        ]
        .map(String::from)
        .to_vec();
        if let Some(w) = who {
            argv.extend(["--token-file".to_string(), w.to_string()]);
        }
        argv.extend(args.iter().map(|a| a.to_string()));
        let home = home.clone();
        holger_cli::run_args(argv, move |k| {
            (k == "HOLGER_CLI_HOME").then(|| home.clone())
        })
    };

    // The fed certificate is served and the route is up within a feed tick.
    let mut health = Err(holger_cli::CliError::Local("not asked".into()));
    for _ in 0..100 {
        tokio::time::sleep(Duration::from_millis(100)).await;
        health = cli(None, &["health"]).await;
        if health.is_ok() {
            break;
        }
    }
    let health = health.unwrap_or_else(|e| panic!("health through the front: {e}"));
    assert!(health.contains("status: \"ok\""), "{health}");

    // The person's own credential rides through, and the primary decides.
    let t = token.display().to_string();
    let me = cli(Some(&t), &["whoami"]).await.unwrap();
    assert!(
        me.contains("subject: \"console\"") && me.contains("authenticated: true"),
        "{me}"
    );
    let repos = cli(Some(&t), &["repos"]).await.unwrap();
    assert!(repos.contains("name: \"files\""), "{repos}");
    // A refusal comes back with its code in the trailers, carried as it came.
    match cli(None, &["snapshot"]).await {
        Err(holger_cli::CliError::Server(s)) => assert!(
            matches!(
                s.code(),
                tonic::Code::Unauthenticated | tonic::Code::PermissionDenied
            ),
            "{s:?}"
        ),
        other => panic!("an anonymous snapshot through the front: {other:?}"),
    }
    // The primary audited the call it served.
    let mut audit = String::new();
    for e in std::fs::read_dir(pdir.path().join("audit"))
        .unwrap()
        .flatten()
    {
        audit.push_str(&String::from_utf8_lossy(
            &std::fs::read(e.path()).unwrap_or_default(),
        ));
    }
    assert!(
        !audit.is_empty(),
        "the primary keeps its audit of the calls it served"
    );
}