From 8b57c16f0a0b205631cc8afe5e01d54f3dd5bc07 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Fri, 22 Mar 2024 23:59:02 +0000 Subject: [PATCH] krata: fix conformance to container startup --- DEV.md | 6 +- crates/kratad/src/reconcile/guest.rs | 5 +- crates/krataguest/src/init.rs | 15 ++++- crates/kratart/src/image/compiler.rs | 83 +++++++++++++++++++--------- crates/kratart/src/image/name.rs | 2 +- 5 files changed, 77 insertions(+), 34 deletions(-) diff --git a/DEV.md b/DEV.md index aa063e6..9caf8b9 100644 --- a/DEV.md +++ b/DEV.md @@ -2,7 +2,7 @@ ## Structure -krata is composed of three major executables: +krata is composed of four major executables: | Executable | Runs On | User Interaction | Dev Runner | Code Path | | ---------- | ------- | ---------------- | ------------------------ | ----------------- | @@ -54,10 +54,10 @@ $ cd krata 6. Build a guest kernel image: ```sh -$ ./hack/kernel/build.sh -j4 +$ ./hack/kernel/build.sh ``` -7. Copy the guest kernel image at `target/kernel/kernel` to `/var/lib/krata/guest/kernel` to have it automatically detected by kratad. +7. Copy the guest kernel image at `target/kernel/kernel-x86_64` to `/var/lib/krata/guest/kernel` to have it automatically detected by kratad. 8. Launch `./hack/debug/kratanet.sh` and keep it running in the foreground. 9. Launch `./hack/debug/kratad.sh` and keep it running in the foreground. 10. Run kratactl to launch a guest: diff --git a/crates/kratad/src/reconcile/guest.rs b/crates/kratad/src/reconcile/guest.rs index 1daa934..79bfbb5 100644 --- a/crates/kratad/src/reconcile/guest.rs +++ b/crates/kratad/src/reconcile/guest.rs @@ -191,7 +191,10 @@ impl GuestReconciler { } async fn destroy(&self, uuid: Uuid, guest: &mut Guest) -> Result { - self.runtime.destroy(uuid).await?; + if let Err(error) = self.runtime.destroy(uuid).await { + warn!("failed to destroy runtime guest {}: {}", uuid, error); + } + info!("destroyed guest {}", uuid); guest.network = None; guest.state = Some(GuestState { diff --git a/crates/krataguest/src/init.rs b/crates/krataguest/src/init.rs index 3f13f4a..e2f65cc 100644 --- a/crates/krataguest/src/init.rs +++ b/crates/krataguest/src/init.rs @@ -106,6 +106,7 @@ impl GuestInit { self.mount_kernel_fs("devtmpfs", "/dev", "mode=0755")?; self.mount_kernel_fs("proc", "/proc", "")?; self.mount_kernel_fs("sysfs", "/sys", "")?; + std::os::unix::fs::symlink("/proc/self/fd", "/dev/fd")?; Ok(()) } @@ -391,6 +392,12 @@ impl GuestInit { cmd = launch.run.as_ref().unwrap().clone(); } + if let Some(entrypoint) = config.entrypoint() { + for item in entrypoint.iter().rev() { + cmd.insert(0, item.to_string()); + } + } + if cmd.is_empty() { cmd.push("/bin/sh".to_string()); } @@ -433,8 +440,7 @@ impl GuestInit { working_dir = "/".to_string(); } - std::env::set_current_dir(&working_dir)?; - self.fork_and_exec(path, cmd, env).await?; + self.fork_and_exec(working_dir, path, cmd, env).await?; Ok(()) } @@ -485,23 +491,26 @@ impl GuestInit { async fn fork_and_exec( &mut self, + working_dir: String, path: CString, cmd: Vec, env: Vec, ) -> Result<()> { match unsafe { fork()? } { ForkResult::Parent { child } => self.background(child).await, - ForkResult::Child => self.foreground(path, cmd, env).await, + ForkResult::Child => self.foreground(working_dir, path, cmd, env).await, } } async fn foreground( &mut self, + working_dir: String, path: CString, cmd: Vec, env: Vec, ) -> Result<()> { GuestInit::set_controlling_terminal()?; + std::env::set_current_dir(working_dir)?; execve(&path, &cmd, &env)?; Ok(()) } diff --git a/crates/kratart/src/image/compiler.rs b/crates/kratart/src/image/compiler.rs index 45ef050..05fb28f 100644 --- a/crates/kratart/src/image/compiler.rs +++ b/crates/kratart/src/image/compiler.rs @@ -7,7 +7,7 @@ use backhand::{FilesystemCompressor, FilesystemWriter, NodeHeader}; use log::{debug, trace, warn}; use oci_spec::image::{ImageConfiguration, ImageManifest}; use std::fs::File; -use std::io::{BufReader, Cursor}; +use std::io::{ErrorKind, Read}; use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; use std::path::{Path, PathBuf}; use std::pin::Pin; @@ -20,18 +20,7 @@ use walkdir::WalkDir; use crate::image::fetch::{OciImageDownloader, OciImageLayer}; -pub const IMAGE_SQUASHFS_VERSION: u64 = 1; -const LAYER_BUFFER_SIZE: usize = 128 * 1024; - -// we utilize in-memory buffers when generating the squashfs for files -// under this size. for files of or above this size, we open a file. -// the file is then read during writing. we want to reduce the number -// of open files during squashfs generation, so this limit should be set -// to something that limits the number of files on average, at the expense -// of increased memory usage. -// TODO: it may be wise to, during crawling of the image layers, infer this -// value from the size to file count ratio of all layers. -const SQUASHFS_MEMORY_BUFFER_LIMIT: usize = 8 * 1024 * 1024; +pub const IMAGE_SQUASHFS_VERSION: u64 = 2; pub struct ImageInfo { pub image_squashfs: PathBuf, @@ -238,13 +227,24 @@ impl ImageCompiler<'_> { layer: &OciImageLayer, image_dir: &Path, ) -> Result<()> { + let uid = entry.header().uid()?; + let gid = entry.header().gid()?; trace!( - "unpack entry layer={} path={:?} type={:?}", + "unpack entry layer={} path={:?} type={:?} uid={} gid={}", &layer.digest, entry.path()?, - entry.header().entry_type() + entry.header().entry_type(), + uid, + gid, ); - entry.unpack_in(image_dir).await?; + entry.set_preserve_mtime(true); + entry.set_preserve_permissions(true); + entry.set_unpack_xattrs(true); + if let Some(path) = entry.unpack_in(image_dir).await? { + if !path.is_symlink() { + std::os::unix::fs::chown(path, Some(uid as u32), Some(gid as u32))?; + } + } Ok(()) } @@ -319,14 +319,7 @@ impl ImageCompiler<'_> { } else if typ.is_dir() { writer.push_dir(rel, header)?; } else if typ.is_file() { - if metadata.size() >= SQUASHFS_MEMORY_BUFFER_LIMIT as u64 { - let reader = - BufReader::with_capacity(LAYER_BUFFER_SIZE, File::open(entry.path())?); - writer.push_file(reader, rel, header)?; - } else { - let cursor = Cursor::new(std::fs::read(entry.path())?); - writer.push_file(cursor, rel, header)?; - } + writer.push_file(ConsumingFileReader::new(entry.path()), rel, header)?; } else if typ.is_block_device() { let device = metadata.dev(); writer.push_block_device(device as u32, rel, header)?; @@ -338,8 +331,6 @@ impl ImageCompiler<'_> { } } - std::fs::remove_dir_all(image_dir)?; - let squash_file_path = squash_file .to_str() .ok_or_else(|| anyhow!("failed to convert squashfs string"))?; @@ -347,6 +338,46 @@ impl ImageCompiler<'_> { let mut file = File::create(squash_file)?; trace!("squash generate: {}", squash_file_path); writer.write(&mut file)?; + std::fs::remove_dir_all(image_dir)?; Ok(()) } } + +struct ConsumingFileReader { + path: PathBuf, + file: Option, +} + +impl ConsumingFileReader { + fn new(path: &Path) -> ConsumingFileReader { + ConsumingFileReader { + path: path.to_path_buf(), + file: None, + } + } +} + +impl Read for ConsumingFileReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + if self.file.is_none() { + self.file = Some(File::open(&self.path)?); + } + let Some(ref mut file) = self.file else { + return Err(std::io::Error::new( + ErrorKind::NotFound, + "file was not opened", + )); + }; + file.read(buf) + } +} + +impl Drop for ConsumingFileReader { + fn drop(&mut self) { + let file = self.file.take(); + drop(file); + if let Err(error) = std::fs::remove_file(&self.path) { + warn!("failed to delete consuming file {:?}: {}", self.path, error); + } + } +} diff --git a/crates/kratart/src/image/name.rs b/crates/kratart/src/image/name.rs index ecdb036..8dffbcf 100644 --- a/crates/kratart/src/image/name.rs +++ b/crates/kratart/src/image/name.rs @@ -2,7 +2,7 @@ use anyhow::Result; use std::fmt; use url::Url; -const DOCKER_HUB_MIRROR: &str = "registry.docker.io"; +const DOCKER_HUB_MIRROR: &str = "mirror.gcr.io"; const DEFAULT_IMAGE_TAG: &str = "latest"; #[derive(Debug, Clone, PartialEq, Eq, Hash)]