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"
);
}