Skip to content
Open
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
7 changes: 4 additions & 3 deletions .forgejo/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ jobs:
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: forkd microVM CI (zen-gates gate_run)
- name: forkd microVM CI (gate_run)
env:
ZEN_GATES_URL: ${{ secrets.ZEN_GATES_URL }}
ZEN_GATES_TOKEN: ${{ secrets.ZEN_GATES_TOKEN }}
FORKD_URL: ${{ secrets.FORKD_URL }}
FORKD_TOKEN: ${{ secrets.FORKD_TOKEN }}
FORKD_SNAPSHOT_TAG: ${{ vars.FORKD_SNAPSHOT_TAG }}
run: |
if [ -x ./.forgejo/gates/forkd-ci.sh ]; then ./.forgejo/gates/forkd-ci.sh
else echo "forkd-ci not yet wired (cal/forkd plane)"; exit 1; fi
Expand Down
2 changes: 1 addition & 1 deletion lib/components/fabro-referee/src/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub enum BackendKind {
impl BackendKind {
pub fn from_str_loose(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"forkd" | "controller" | "zen-gates" => Some(Self::Forkd),
"forkd" | "controller" => Some(Self::Forkd),
"hermetic" | "local" | "fallback" => Some(Self::Hermetic),
"fake" | "test" | "mock" => Some(Self::Fake),
_ => None,
Expand Down
55 changes: 46 additions & 9 deletions lib/components/fabro-referee/src/gate/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,42 @@ use tracing::{info, warn};
use super::{GateBackend, GateOutput};
use crate::types::{Acceptance, TaskSpec, Verdict};

const FORKD_TOKEN_FILE: &str = "/home/vvladescu/fabro-run/.forkd-token";
/// Default forkd token file path. Used only when the `FORKD_TOKEN_FILE`
/// env var is unset or empty. Keep this a generic, non-host-specific
/// example; operators must override it for their deployment.
const FORKD_TOKEN_FILE_DEFAULT: &str = "/etc/forkd/token";

/// Default forkd snapshot tag. Used only when the `FORKD_SNAPSHOT_TAG`
/// env var is unset or empty. Keep this a generic, non-host-specific
/// example; the value here is the example the public spec uses.
const FORKD_SNAPSHOT_TAG_DEFAULT: &str = "forkd-base";

/// Resolve the path to the forkd bearer-token file. The operator can
/// override the default with the `FORKD_TOKEN_FILE` env var. The
/// returned path is the one [`forkd_token`] will read.
#[expect(
clippy::disallowed_methods,
reason = "sync scorer binary: one-shot environment lookup for the token-file path"
)]
pub fn forkd_token_file() -> std::path::PathBuf {
match std::env::var("FORKD_TOKEN_FILE") {
Ok(value) if !value.trim().is_empty() => std::path::PathBuf::from(value.trim()),
_ => std::path::PathBuf::from(FORKD_TOKEN_FILE_DEFAULT),
}
}

/// Resolve the default forkd snapshot tag. The operator can override
/// the default with the `FORKD_SNAPSHOT_TAG` env var.
#[expect(
clippy::disallowed_methods,
reason = "sync scorer binary: one-shot environment lookup for the default snapshot tag"
)]
pub fn forkd_snapshot_tag() -> String {
match std::env::var("FORKD_SNAPSHOT_TAG") {
Ok(value) if !value.trim().is_empty() => value.trim().to_string(),
_ => FORKD_SNAPSHOT_TAG_DEFAULT.to_string(),
}
}

/// Resolve the forkd bearer token without exposing its value in errors/logs.
#[expect(
Expand All @@ -36,7 +71,8 @@ pub fn forkd_token() -> Result<String> {
}
}

match std::fs::read_to_string(FORKD_TOKEN_FILE) {
let token_path = forkd_token_file();
match std::fs::read_to_string(&token_path) {
Ok(contents) => {
let token = contents.trim().to_string();
if !token.is_empty() {
Expand All @@ -45,18 +81,19 @@ pub fn forkd_token() -> Result<String> {
}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => {
return Err(error).with_context(|| format!("read forkd token file {FORKD_TOKEN_FILE}"));
return Err(error).with_context(|| format!("read forkd token file {}", token_path.display()));
}
}

bail!("forkd token unavailable: set FORKD_TOKEN or provide {FORKD_TOKEN_FILE}")
bail!("forkd token unavailable: set FORKD_TOKEN or provide {}", token_path.display())
}

/// The real forkd controller at `forkd.internal.example:8891`. The scorer only uses the
/// existing score/read path: it creates a `zen-gate-base` sandbox, executes
/// the apply-and-acceptance pipeline, and deletes that sandbox. It never
/// activates, reconfigures, restarts, or re-baselines the controller or its
/// golden rootfs.
/// existing score/read path: it creates a snapshot from the configured
/// tag (env `FORKD_SNAPSHOT_TAG`, default "forkd-base"), executes the
/// apply-and-acceptance pipeline, and deletes that sandbox. It never
/// activates, reconfigures, restarts, or re-baselines the controller or
/// its golden rootfs.
pub struct ForkdController {
/// The controller endpoint, e.g. `http://forkd.internal.example:8891`.
endpoint: String,
Expand Down Expand Up @@ -101,7 +138,7 @@ impl GateBackend for ForkdController {

// 1. create a sandbox from the golden snapshot.
let create = auth(self.client.post(format!("{base}/v1/sandboxes")))
.json(&serde_json::json!({ "snapshot_tag": "zen-gate-base" }))
.json(&serde_json::json!({ "snapshot_tag": forkd_snapshot_tag() }))
.send()
.with_context(|| format!("forkd create sandbox @ {base}"))?;
let cstatus = create.status();
Expand Down
5 changes: 3 additions & 2 deletions lib/components/fabro-sandbox/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ pub enum ForkdNetwork {
#[cfg(feature = "forkd")]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ForkdSettings {
/// forkd snapshot tag to boot from (e.g. `"zen-gate-base"`).
/// Resolved from `FORKD_SNAPSHOT_TAG` env var; default `"zen-gate-base"`.
/// forkd snapshot tag to boot from (e.g. `"forkd-base"`).
/// Resolved from `FORKD_SNAPSHOT_TAG` env var; default
/// `"forkd-base"` (see [`crate::forkd::DEFAULT_SNAPSHOT_TAG`]).
#[serde(default = "ForkdSettings::default_snapshot_tag")]
pub snapshot_tag: String,
/// Legacy VM image/kernel/memory settings — retained for deserialization
Expand Down
4 changes: 3 additions & 1 deletion lib/components/fabro-sandbox/src/forkd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ pub(crate) const REPOS_ROOT: &str = "/home/fabro/repos";
const PROVIDER: &str = "forkd";

/// Default snapshot tag used when `FORKD_SNAPSHOT_TAG` is not set.
pub const DEFAULT_SNAPSHOT_TAG: &str = "zen-gate-base";
/// Generic, non-host-specific example value; operators override via
/// the `FORKD_SNAPSHOT_TAG` env var or per-sandbox `ForkdSettings`.
pub const DEFAULT_SNAPSHOT_TAG: &str = "forkd-base";

/// Maximum number of retry attempts for transient HTTP failures (5xx /
/// connect).
Expand Down
8 changes: 4 additions & 4 deletions specs/forkd-e2e/behavior.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Feature: forkd end-to-end capability
@task-create-sandbox
Scenario: creating a sandbox from a snapshot tag returns a server-assigned id
Given the forkd service is reachable
And a snapshot tag "zen-gate-base" exists in the snapshot registry
When I send a create-sandbox request with snapshot_tag "zen-gate-base"
And a snapshot tag "forkd-base" exists in the snapshot registry
When I send a create-sandbox request with snapshot_tag "forkd-base"
Then the response status is 201
And the response body contains a non-empty "id" field
And the returned id matches the pattern "[a-f0-9-]{36}"
Expand Down Expand Up @@ -54,8 +54,8 @@ Feature: forkd end-to-end capability
@task-real-workflow
Scenario: a real workflow executing git clone and running a command completes inside the microVM
Given the forkd service is reachable
And a snapshot tag "zen-gate-base" exists in the snapshot registry
When I create a sandbox from snapshot_tag "zen-gate-base" and store its id as "wf_sandbox_id"
And a snapshot tag "forkd-base" exists in the snapshot registry
When I create a sandbox from snapshot_tag "forkd-base" and store its id as "wf_sandbox_id"
And I exec in sandbox "wf_sandbox_id" with argv ["git", "clone", "--depth", "1", "https://github.com/nicowillis/hello-world.git", "/tmp/repo"]
Then the exec response exit_code is 0
And the sandbox filesystem path "/tmp/repo" exists
Expand Down