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

nordisk/holger

server/lib/tests/only_the_start_brings_a_tunnel_up.rs

//! ★ **Only the start brings a WireGuard tunnel up** (HOL-PRODUCTS-9).
//!
//! Building the intake chain is what `holger sync key`, `sync invalidate`, the export and
//! import doors and the start all do. A downstream's WireGuard tunnel to its upstream is the
//! start's alone: building the chain mints no WireGuard key, takes no invite and says nothing;
//! the start does it, once, for the process it serves in.
#![cfg(feature = "downstream-wg")]

use std::io::Write;

use holger_server_lib::{read_ron_config, wire_holger, Holger};
use traits::test_support::hold_port;

/// The downstream's WireGuard key directory under `data`.
fn wg_dir(data: &std::path::Path) -> std::path::PathBuf {
    data.join(holger_server_lib::sync::STATE_DIR)
        .join(holger_server_lib::sync::tunnel::KEY_DIR)
}

fn holds_a_key(data: &std::path::Path) -> bool {
    std::fs::read_dir(wg_dir(data)).is_ok_and(|mut d| d.next().is_some())
}

#[test]
fn building_the_intake_chain_brings_no_tunnel_up_and_the_start_does() {
    let data = tempfile::tempdir().unwrap();
    let d = data.path().display().to_string();
    let (grpc, http) = (hold_port(), hold_port());
    let repo = |name: &str| {
        format!(
            r#"(ron_name: "{name}", 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}/{name}")),"#
        )
    };
    // A downstream build is one-way by default: this one fetches from its one upstream.
    let boundary = if cfg!(feature = "gateway") {
        "boundary: Fetch,"
    } else {
        ""
    };
    let ron = format!(
        r#"(
    {boundary}
    repositories: [ {incoming} {quarantine} {approved} {production} ],
    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),
    promotion: Some((
        ladder_name: "intake",
        stages: [
            (name: "incoming", repo: "incoming"), (name: "quarantine", repo: "quarantine"),
            (name: "approved", repo: "approved"), (name: "production", repo: "production"),
        ],
        intake: Some((
            incoming: "incoming", quarantine: "quarantine", approved: "approved", production: "production",
            approvals: 1,
            scan: None,
        )),
    )),
    sync: Some((name: "lab-2", upstreams: [
        (name: "cloud", key: "{key}", url: Some("http://10.46.0.1:50070"), invite: Some("{d}/lab-2.invite.json")),
    ])),
)"#,
        incoming = repo("incoming"),
        quarantine = repo("quarantine"),
        approved = repo("approved"),
        production = repo("production"),
        key = holger_sync::SyncKey::from_seed([3; 32]).public_hex(),
    );
    let mut cfg = tempfile::NamedTempFile::new().unwrap();
    cfg.write_all(ron.as_bytes()).unwrap();
    let mut holger: Holger = read_ron_config(cfg.path()).expect("the config parses");
    holger.instantiate_backends().expect("backends");
    wire_holger(&mut holger).expect("wire");

    // What `holger sync key` does: the chain, and its hub.
    let audit = holger.audit_log().expect("audit");
    let chain = holger
        .intake_chain(audit)
        .expect("the intake chain")
        .expect("this config declares one");
    assert!(chain.sync().is_some(), "the chain carries the sync hub");
    assert!(
        !holds_a_key(data.path()),
        "building the intake chain minted the downstream's WireGuard key ({}): a `sync key` \
         brought the tunnel up",
        wg_dir(data.path()).display()
    );

    // The start brings it up (here: no invite yet, no CAP_NET_ADMIN; the key is minted and
    // the start says what the upstream needs, and the box serves on).
    let rt = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(2)
        .enable_all()
        .build()
        .unwrap();
    {
        let _in = rt.enter();
        holger.start().expect("start");
    }
    assert!(
        holds_a_key(data.path()),
        "the start brings the tunnel up from its invite: it holds the WireGuard key"
    );
}