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
@@ -129,6 +129,19 @@ impl RootfsCache {
|
||||
format!("Failed to create cache directory {}", self.dir.display())
|
||||
})?;
|
||||
download_image(&source, &cache_path, &arch, progress)?;
|
||||
// Whatever was provisioned belonged to the entry this just
|
||||
// replaced. A stale marker here would present the pristine
|
||||
// image as provisioned and skip the next provisioning for
|
||||
// good.
|
||||
let marker = sidecar(&cache_path, PROVISIONED_SUFFIX);
|
||||
if marker.exists() {
|
||||
std::fs::remove_file(&marker).with_context(|| {
|
||||
format!(
|
||||
"Failed to drop the stale provisioned marker {}",
|
||||
marker.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
} else {
|
||||
log::debug!("Using cached tarball: {}", cache_path.display());
|
||||
}
|
||||
|
||||
+16
-9
@@ -91,6 +91,21 @@ impl Default for RunOptions {
|
||||
|
||||
/// Run one machine to completion and return the payload's exit code.
|
||||
pub fn run(options: &RunOptions, progress: &dyn Progress) -> Result<i32> {
|
||||
let mut request = PrepareRequest::new(options.image.clone());
|
||||
request.arch = options.arch.clone();
|
||||
request.no_cache = options.no_cache;
|
||||
let cache = RootfsCache::new(RootfsCache::default_dir()?);
|
||||
let rootfs = cache
|
||||
.prepare(&request, progress)
|
||||
.context("preparing the rootfs")?;
|
||||
run_prepared(&rootfs, options)
|
||||
}
|
||||
|
||||
/// Boot one machine over an already-prepared rootfs. This is the whole
|
||||
/// boot half of [`run`], and the engine the provisioner drives: it
|
||||
/// wants a machine over the extracted tree it is about to persist,
|
||||
/// before that tree becomes the cache entry every later boot sees.
|
||||
pub fn run_prepared(rootfs: &PreparedRootfs, options: &RunOptions) -> Result<i32> {
|
||||
if options.image.is_empty() {
|
||||
return Err(Error::Other(anyhow!("no image given")));
|
||||
}
|
||||
@@ -101,14 +116,6 @@ pub fn run(options: &RunOptions, progress: &dyn Progress) -> Result<i32> {
|
||||
let ram_mib = parse_memory_mib(&options.memory).map_err(Error::Other)?;
|
||||
let vcpus = vcpus();
|
||||
|
||||
let mut request = PrepareRequest::new(options.image.clone());
|
||||
request.arch = options.arch.clone();
|
||||
request.no_cache = options.no_cache;
|
||||
let cache = RootfsCache::new(RootfsCache::default_dir()?);
|
||||
let rootfs = cache
|
||||
.prepare(&request, progress)
|
||||
.context("preparing the rootfs")?;
|
||||
|
||||
let hostname = if options.hostname.is_empty() {
|
||||
format!("ecrou-vm-{}", utils::random_suffix(HOSTNAME_SUFFIX_LEN))
|
||||
} else {
|
||||
@@ -129,7 +136,7 @@ pub fn run(options: &RunOptions, progress: &dyn Progress) -> Result<i32> {
|
||||
})
|
||||
.transpose()
|
||||
.map_err(Error::Other)?;
|
||||
let share_pairs = collect_shares(&rootfs, options, capture_share).map_err(Error::Other)?;
|
||||
let share_pairs = collect_shares(rootfs, options, capture_share).map_err(Error::Other)?;
|
||||
let shares = share_pairs
|
||||
.iter()
|
||||
.map(|(_, gs)| gs.clone())
|
||||
|
||||
Reference in new issue
Block a user