mirror of
https://github.com/vhaudiquet/ecrou.git
synced 2026-11-12 22:22:44 +00:00
feat(cli): ecrou provision bakes a command into a cache entry
prepare_provisioned and persist were sitting there with no consumer: the machinery for "install once, every later boot carries it" that one-shot machines cannot do on their own - packages, users, anything short of a share - and no way to reach it. ecrou provision IMAGE -- COMMAND is the missing surface. It boots a machine over a freshly prepared rootfs, runs the command, and persists the tree into the cache entry, marked provisioned; a marked entry is extracted as-is on every later call, the command skipped. A failing command leaves the entry unprovisioned for the next attempt, and --no-cache re-downloads before re-provisioning. The engine behind run splits out as vm::run_prepared: the boot half of run, taking the rootfs the provisioner is about to persist. run itself is prepare plus that. One cache fix the provision flow surfaced: a digest refresh replaced the tarball but left the old .provisioned marker, so a fresh pristine image would present as provisioned and skip provisioning for good. The marker now dies with the entry it belonged to.
This commit is contained in:
1 parent
1fb3f0c076
commit
d9f9c79dc4
5 files changed
+117
-9
No files matched your search
@@ -18,10 +18,69 @@ fn main() -> std::process::ExitCode {
|
||||
logger::init(cli.verbose);
|
||||
match cli.command {
|
||||
Command::Run(args) => cmd_run(&args, cli.verbose),
|
||||
Command::Provision {
|
||||
image,
|
||||
no_cache,
|
||||
command,
|
||||
} => cmd_provision(&image, no_cache, &command, cli.verbose),
|
||||
Command::Probe => cmd_probe(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_provision(
|
||||
image: &str,
|
||||
no_cache: bool,
|
||||
command: &[String],
|
||||
cli_verbose: bool,
|
||||
) -> std::process::ExitCode {
|
||||
use ecrou::rootfs::{PrepareRequest, RootfsCache};
|
||||
|
||||
if command.is_empty() {
|
||||
return fail(&anyhow::anyhow!(
|
||||
"provision needs a command: ecrou provision <IMAGE> -- <COMMAND...>"
|
||||
));
|
||||
}
|
||||
let level = if cli_verbose {
|
||||
LogLevel::Verbose
|
||||
} else {
|
||||
LogLevel::Normal
|
||||
};
|
||||
let mut options = RunOptions {
|
||||
image: image.to_owned(),
|
||||
command: command.to_owned(),
|
||||
log_level: level,
|
||||
// The machine exists to change the rootfs, not to cross a
|
||||
// share: the workspace would ride the persist into the cache.
|
||||
..RunOptions::default()
|
||||
};
|
||||
options.workdir = "/".to_owned();
|
||||
|
||||
let progress: &dyn Progress = &TermProgress::new();
|
||||
let request = PrepareRequest {
|
||||
image: image.to_owned(),
|
||||
arch: String::new(),
|
||||
no_cache,
|
||||
};
|
||||
let dir = match RootfsCache::default_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(err) => return fail(&anyhow::Error::new(err)),
|
||||
};
|
||||
let cache = RootfsCache::new(dir);
|
||||
let outcome = cache.prepare_provisioned(&request, progress, |rootfs| {
|
||||
let code = ecrou::vm::run_prepared(rootfs, &options)?;
|
||||
if code != 0 {
|
||||
anyhow::bail!(
|
||||
"the provisioning command exited {code}; the cache entry was left unprovisioned"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
match outcome {
|
||||
Ok(_rootfs) => std::process::ExitCode::SUCCESS,
|
||||
Err(err) => fail(&anyhow::Error::new(err)),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_run(args: &RunArgs, cli_verbose: bool) -> std::process::ExitCode {
|
||||
let mut options = RunOptions {
|
||||
image: args.image.clone(),
|
||||
|
||||
Reference in new issue
Block a user