Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions crates/xtask-registry-smoke/src/servers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::time::Duration;

use anyhow::{Context, Result, bail, ensure};
use serde_json::{Map, Value};
use tempfile::{NamedTempFile, TempPath};
use tempfile::{NamedTempFile, TempDir, TempPath};
use xtask_registry_admin::{output, registry_dir, results, status, wrangler};

use crate::bytes::sha256_hex;
Expand Down Expand Up @@ -334,6 +334,34 @@ fn result_sets(json: &str) -> Result<Vec<Value>> {
.collect()
}

/// The website role's worker. One deployed Worker serves both
/// hostnames, so this one only hands each request, Host header and all,
/// to the registry role's worker through wrangler's dev registry: one
/// workerd then opens the governor's SQLite, D1 and R2, where two over
/// the same files fail writes with `database is locked` whenever they
/// overlap.
const WEB_WORKER: &str =
"export default { fetch: (request, env) => env.REGISTRY.fetch(request) };\n";

/// [`WEB_WORKER`]'s config; the service is `wrangler.jsonc`'s worker.
const WEB_CONFIG: &str = r#"{
"name": "cabin-registry-smoke-web",
"main": "index.js",
"compatibility_date": "2026-07-09",
"services": [{ "binding": "REGISTRY", "service": "cabin-registry" }]
}
"#;

/// Writes [`WEB_WORKER`] and [`WEB_CONFIG`] into `dir`.
///
/// # Errors
///
/// If either file cannot be written.
fn write_web_worker(dir: &Path) -> Result<()> {
fs::write(dir.join("index.js"), WEB_WORKER).context("write the website-role worker")?;
fs::write(dir.join("wrangler.json"), WEB_CONFIG).context("write the website-role config")
}

/// The four servers, their logs, and the `.dev.vars` they are
/// configured by - one owner, so one teardown orders the cleanup
/// across them.
Expand All @@ -355,6 +383,12 @@ pub struct DevServers {
/// path restores it with `rename(2)`, which cannot cross a
/// filesystem (`registry/.gitignore` covers `/.dev.vars*`).
dev_vars_backup: Option<TempPath>,
/// The website role's forwarding worker (`WEB_WORKER`).
web_worker: TempDir,
/// This run's wrangler dev registry, through which the website
/// role reaches the registry role's worker; per run, so no other
/// local `wrangler dev` answers for that worker.
dev_registry: TempDir,
}

impl DevServers {
Expand Down Expand Up @@ -383,10 +417,13 @@ impl DevServers {
dev_vars: registry_dir().join(".dev.vars"),
dev_vars_created: false,
dev_vars_backup: None,
web_worker: TempDir::new().context("create the website-role worker directory")?,
dev_registry: TempDir::new().context("create the wrangler dev registry")?,
};
// Every `?` from here drops `servers`, which is the teardown.
servers.start_mocks(mock_dir)?;
servers.write_dev_vars()?;
write_web_worker(servers.web_worker.path())?;
servers.refuse_stale_ports()?;
servers
.start_dev_servers()
Expand Down Expand Up @@ -419,6 +456,7 @@ impl DevServers {
/// `/healthz`.
pub fn start_registry_dev(&mut self) -> Result<()> {
let mut command = wrangler(&["dev", "--port", &self.ports.registry, "--test-scheduled"]);
command.env("WRANGLER_REGISTRY_PATH", self.dev_registry.path());
log_to(&mut command, self.dev_log.path())?;
self.registry = Some(xtask_ci::spawn_tracked(&mut command).context("start wrangler dev")?);
let url = format!("{}/healthz", self.ports.base());
Expand All @@ -430,17 +468,32 @@ impl DevServers {
)
}

/// The website-role instance: same code, same local state, but
/// wrangler pins its emulated Host header to cabinpkg.com, which is
/// what flips the Worker's role dispatch. `/healthz` only exists
/// on the registry role, so any HTTP status at all (the website
/// role answers it 401/404) proves the instance is up.
/// The website-role instance: a forwarder into the registry role's
/// worker (`WEB_WORKER`), with wrangler pinning its emulated Host
/// header to cabinpkg.com, which is what flips the Worker's role
/// dispatch.
/// `/healthz` only exists on the registry role, so any HTTP status
/// at all (the website role answers it 401/404) proves the
/// instance is up.
///
/// # Errors
///
/// If the child cannot be spawned, exits early, or never answers.
pub fn start_web_dev(&mut self) -> Result<()> {
let mut command = wrangler(&["dev", "--port", &self.ports.web, "--host", "cabinpkg.com"]);
let config = self.web_worker.path().join("wrangler.json");
let config = config
.to_str()
.context("the scratch directory is not UTF-8")?;
let mut command = wrangler(&[
"dev",
"--config",
config,
"--port",
&self.ports.web,
"--host",
"cabinpkg.com",
]);
command.env("WRANGLER_REGISTRY_PATH", self.dev_registry.path());
log_to(&mut command, self.web_log.path())?;
self.web = Some(
xtask_ci::spawn_tracked(&mut command).context("start the website-role wrangler dev")?,
Expand Down
7 changes: 5 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,8 +645,11 @@ is a change to every consumer's cancellation story.

Repository-owned maintainer tool (`publish = false`) holding the registry smoke test, reached
through the `cargo registry-smoke` alias. It drives two local `wrangler dev` instances (the
registry role and the website role) over one local D1/R2 state, plus a local export-API mock and
a local GitHub mock, through a fixed step sequence with per-step diagnostics. `registry.yml`
registry role and the website role), plus a local export-API mock and a local GitHub mock,
through a fixed step sequence with per-step diagnostics. The website role only forwards to the
registry role's worker through wrangler's dev registry, so one workerd opens the local D1, R2 and
governor state, as one deployed Worker serves both hostnames: two over the same SQLite files fail
writes with `database is locked`. `registry.yml`
runs it after the Worker build on every registry-touching pull request and merge-queue run
(the `registry` filter in `.github/path-filters.yml`), so it is the one check that executes the
wasm32 glue before a deploy. One leg runs the real verification driver (`xtask-registry-admin`'s
Expand Down
6 changes: 4 additions & 2 deletions registry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ cargo kani # proof harnesses (`cargo install kani
(cd .. && CABIN_REGISTRY_SMOKE_TOKEN=cabin_smoke cargo registry-smoke) # end-to-end, local
```

`cargo registry-smoke` runs two `wrangler dev` instances over shared local
D1/R2 state under `.wrangler/` - one per hostname role - and checks
`cargo registry-smoke` runs two `wrangler dev` instances - one per hostname
role, the website role's only forwarding to the registry role's worker,
which alone opens the local D1, R2 and governor state under `.wrangler/` -
and checks
`/healthz`, the mutation surface's uniform `401` with its byte-identical
`WWW-Authenticate` challenge, the hostname dispatch (no read plane on the
website origin, no API surface on the registry domain), the OAuth and
Expand Down
Loading