mirror of
https://github.com/marcoallegretti/karapace.git
synced 2026-03-26 21:43:09 +00:00
runtime: propagate offline mode
Add RuntimeSpec.offline and thread it through OCI/namespace backends. Offline mode requires cached base images, forces sandbox network isolation, and fails fast when system package resolution/installation would require network access.
This commit is contained in:
parent
32296bd75a
commit
cbf954bead
5 changed files with 47 additions and 8 deletions
|
|
@ -9,6 +9,8 @@ pub struct RuntimeSpec {
|
|||
pub overlay_path: String,
|
||||
pub store_root: String,
|
||||
pub manifest: NormalizedManifest,
|
||||
#[serde(default)]
|
||||
pub offline: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ pub struct ResolvedImage {
|
|||
pub display_name: String,
|
||||
}
|
||||
|
||||
pub fn resolve_pinned_image_url(name: &str) -> Result<String, RuntimeError> {
|
||||
let resolved = resolve_image(name)?;
|
||||
download_url(&resolved.source)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn resolve_image(name: &str) -> Result<ResolvedImage, RuntimeError> {
|
||||
let name = name.trim().to_lowercase();
|
||||
|
|
@ -248,6 +253,7 @@ impl ImageCache {
|
|||
&self,
|
||||
resolved: &ResolvedImage,
|
||||
progress: &dyn Fn(&str),
|
||||
offline: bool,
|
||||
) -> Result<PathBuf, RuntimeError> {
|
||||
let rootfs = self.rootfs_path(&resolved.cache_key);
|
||||
if self.is_cached(&resolved.cache_key) {
|
||||
|
|
@ -255,6 +261,13 @@ impl ImageCache {
|
|||
return Ok(rootfs);
|
||||
}
|
||||
|
||||
if offline {
|
||||
return Err(RuntimeError::ExecFailed(format!(
|
||||
"offline mode: base image '{}' is not cached",
|
||||
resolved.display_name
|
||||
)));
|
||||
}
|
||||
|
||||
std::fs::create_dir_all(&rootfs)?;
|
||||
|
||||
progress(&format!(
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ image = "rolling"
|
|||
overlay_path: dir.join("overlay").to_string_lossy().to_string(),
|
||||
store_root: dir.to_string_lossy().to_string(),
|
||||
manifest,
|
||||
offline: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,6 +218,7 @@ backend = "mock"
|
|||
overlay_path: dir.path().join("overlay").to_string_lossy().to_string(),
|
||||
store_root: dir.path().to_string_lossy().to_string(),
|
||||
manifest,
|
||||
offline: false,
|
||||
};
|
||||
|
||||
let backend = MockBackend::new();
|
||||
|
|
|
|||
|
|
@ -62,11 +62,17 @@ impl RuntimeBackend for NamespaceBackend {
|
|||
// Download/cache the base image
|
||||
let resolved = resolve_image(&spec.manifest.base_image)?;
|
||||
let image_cache = ImageCache::new(&self.store_root);
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress)?;
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress, spec.offline)?;
|
||||
|
||||
// Compute content digest of the base image
|
||||
let base_image_digest = compute_image_digest(&rootfs)?;
|
||||
|
||||
if spec.offline && !spec.manifest.system_packages.is_empty() {
|
||||
return Err(RuntimeError::ExecFailed(
|
||||
"offline mode: cannot resolve system packages".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
// If there are packages to resolve, set up a temporary overlay
|
||||
// and install+query to get exact versions
|
||||
let resolved_packages = if spec.manifest.system_packages.is_empty() {
|
||||
|
|
@ -145,11 +151,11 @@ impl RuntimeBackend for NamespaceBackend {
|
|||
// Resolve and download the base image
|
||||
let resolved = resolve_image(&spec.manifest.base_image)?;
|
||||
let image_cache = ImageCache::new(&self.store_root);
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress)?;
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress, spec.offline)?;
|
||||
|
||||
// Set up overlay filesystem
|
||||
let mut sandbox = SandboxConfig::new(rootfs.clone(), &spec.env_id, &env_dir);
|
||||
sandbox.isolate_network = spec.manifest.network_isolation;
|
||||
sandbox.isolate_network = spec.offline || spec.manifest.network_isolation;
|
||||
|
||||
mount_overlay(&sandbox)?;
|
||||
|
||||
|
|
@ -158,6 +164,11 @@ impl RuntimeBackend for NamespaceBackend {
|
|||
|
||||
// Install system packages if any
|
||||
if !spec.manifest.system_packages.is_empty() {
|
||||
if spec.offline {
|
||||
return Err(RuntimeError::ExecFailed(
|
||||
"offline mode: cannot install system packages".to_owned(),
|
||||
));
|
||||
}
|
||||
let pkg_mgr = detect_package_manager(&sandbox.overlay_merged)
|
||||
.or_else(|| detect_package_manager(&rootfs))
|
||||
.ok_or_else(|| {
|
||||
|
|
@ -216,7 +227,7 @@ impl RuntimeBackend for NamespaceBackend {
|
|||
|
||||
// Create sandbox config
|
||||
let mut sandbox = SandboxConfig::new(rootfs, &spec.env_id, &env_dir);
|
||||
sandbox.isolate_network = spec.manifest.network_isolation;
|
||||
sandbox.isolate_network = spec.offline || spec.manifest.network_isolation;
|
||||
sandbox.hostname = format!("karapace-{}", &spec.env_id[..12.min(spec.env_id.len())]);
|
||||
|
||||
// Compute host integration (Wayland, PipeWire, GPU, etc.)
|
||||
|
|
|
|||
|
|
@ -173,9 +173,15 @@ impl RuntimeBackend for OciBackend {
|
|||
|
||||
let resolved = resolve_image(&spec.manifest.base_image)?;
|
||||
let image_cache = ImageCache::new(&self.store_root);
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress)?;
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress, spec.offline)?;
|
||||
let base_image_digest = compute_image_digest(&rootfs)?;
|
||||
|
||||
if spec.offline && !spec.manifest.system_packages.is_empty() {
|
||||
return Err(RuntimeError::ExecFailed(
|
||||
"offline mode: cannot resolve system packages".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
let resolved_packages = if spec.manifest.system_packages.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
|
|
@ -250,15 +256,20 @@ impl RuntimeBackend for OciBackend {
|
|||
|
||||
let resolved = resolve_image(&spec.manifest.base_image)?;
|
||||
let image_cache = ImageCache::new(&self.store_root);
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress)?;
|
||||
let rootfs = image_cache.ensure_image(&resolved, &progress, spec.offline)?;
|
||||
|
||||
let mut sandbox = SandboxConfig::new(rootfs.clone(), &spec.env_id, &env_dir);
|
||||
sandbox.isolate_network = spec.manifest.network_isolation;
|
||||
sandbox.isolate_network = spec.offline || spec.manifest.network_isolation;
|
||||
|
||||
mount_overlay(&sandbox)?;
|
||||
setup_container_rootfs(&sandbox)?;
|
||||
|
||||
if !spec.manifest.system_packages.is_empty() {
|
||||
if spec.offline {
|
||||
return Err(RuntimeError::ExecFailed(
|
||||
"offline mode: cannot install system packages".to_owned(),
|
||||
));
|
||||
}
|
||||
let pkg_mgr = detect_package_manager(&sandbox.overlay_merged)
|
||||
.or_else(|| detect_package_manager(&rootfs))
|
||||
.ok_or_else(|| {
|
||||
|
|
@ -319,7 +330,7 @@ impl RuntimeBackend for OciBackend {
|
|||
let rootfs = image_cache.rootfs_path(&resolved.cache_key);
|
||||
|
||||
let mut sandbox = SandboxConfig::new(rootfs, &spec.env_id, &env_dir);
|
||||
sandbox.isolate_network = spec.manifest.network_isolation;
|
||||
sandbox.isolate_network = spec.offline || spec.manifest.network_isolation;
|
||||
|
||||
let host = compute_host_integration(&spec.manifest);
|
||||
sandbox.bind_mounts.extend(host.bind_mounts);
|
||||
|
|
|
|||
Loading…
Reference in a new issue