diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 7a9ae2d960..2e40b8ae2a 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -1202,6 +1202,7 @@ dependencies = [ "oci", "once_cell", "rand", + "safe-path", "serde_json", "slog", "slog-scope", diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index b4a0152b81..e17cd32820 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -1733,6 +1733,7 @@ mod tests { spec: Some(spec), rootless_euid: false, rootless_cgroup: false, + container_name: "".to_string(), } } diff --git a/src/agent/rustjail/src/specconv.rs b/src/agent/rustjail/src/specconv.rs index 0c8bb46db6..ee7f59a3a1 100644 --- a/src/agent/rustjail/src/specconv.rs +++ b/src/agent/rustjail/src/specconv.rs @@ -14,4 +14,5 @@ pub struct CreateOpts { pub spec: Option, pub rootless_euid: bool, pub rootless_cgroup: bool, + pub container_name: String, } diff --git a/src/agent/rustjail/src/validator.rs b/src/agent/rustjail/src/validator.rs index 4955fbf466..1d01c7b072 100644 --- a/src/agent/rustjail/src/validator.rs +++ b/src/agent/rustjail/src/validator.rs @@ -563,6 +563,7 @@ mod tests { rootless_euid: false, rootless_cgroup: false, spec: Some(spec), + container_name: "container1".to_owned(), }; validate(&config).unwrap_err(); diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index 90c031eb71..822a68cb2f 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -91,6 +91,8 @@ use std::io::{BufRead, BufReader, Write}; use std::os::unix::fs::FileExt; use std::path::PathBuf; +use kata_types::k8s; + pub const CONTAINER_BASE: &str = "/run/kata-containers"; const MODPROBE_PATH: &str = "/sbin/modprobe"; @@ -207,6 +209,8 @@ impl AgentService { } }; + let container_name = k8s::container_name(&oci); + info!(sl(), "receive createcontainer, spec: {:?}", &oci); info!( sl(), @@ -265,6 +269,7 @@ impl AgentService { spec: Some(oci.clone()), rootless_euid: false, rootless_cgroup: false, + container_name, }; let mut ctr: LinuxContainer = @@ -293,6 +298,7 @@ impl AgentService { } s.update_shared_pidns(&ctr)?; + s.setup_shared_mounts(&ctr, &req.shared_mounts)?; s.add_container(ctr); info!(sl(), "created container!"); @@ -2018,6 +2024,7 @@ mod tests { spec: Some(spec), rootless_euid: false, rootless_cgroup: false, + container_name: "".to_string(), } } diff --git a/src/agent/src/sandbox.rs b/src/agent/src/sandbox.rs index b4331ed4be..accf364559 100644 --- a/src/agent/src/sandbox.rs +++ b/src/agent/src/sandbox.rs @@ -7,19 +7,24 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; use std::fmt::{Debug, Formatter}; use std::fs; +use std::os::fd::FromRawFd; use std::os::unix::fs::PermissionsExt; use std::path::Path; use std::str::FromStr; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; +use std::time::{Duration, Instant}; use std::{thread, time}; use anyhow::{anyhow, Context, Result}; use kata_types::cpu::CpuSet; use kata_types::mount::StorageDevice; -use libc::pid_t; +use libc::{pid_t, syscall}; +use nix::fcntl::{self, OFlag}; +use nix::sched::{setns, unshare, CloneFlags}; +use nix::sys::stat::Mode; use oci::{Hook, Hooks}; -use protocols::agent::OnlineCPUMemRequest; +use protocols::agent::{OnlineCPUMemRequest, SharedMount}; use regex::Regex; use rustjail::cgroups as rustjail_cgroups; use rustjail::container::BaseContainer; @@ -254,6 +259,12 @@ impl Sandbox { self.containers.get_mut(id) } + pub fn find_container_by_name(&self, name: &str) -> Option<&LinuxContainer> { + self.containers + .values() + .find(|&c| c.config.container_name == name) + } + pub fn find_process(&mut self, pid: pid_t) -> Option<&mut Process> { for (_, c) in self.containers.iter_mut() { if let Some(p) = c.processes.get_mut(&pid) { @@ -403,6 +414,155 @@ impl Sandbox { } }); } + + #[instrument] + pub fn setup_shared_mounts(&self, c: &LinuxContainer, mounts: &Vec) -> Result<()> { + let mut src_ctrs: HashMap = HashMap::new(); + for shared_mount in mounts { + match src_ctrs.get(&shared_mount.src_ctr) { + None => { + if let Some(c) = self.find_container_by_name(&shared_mount.src_ctr) { + src_ctrs.insert(shared_mount.src_ctr.clone(), c.init_process_pid); + } + } + Some(_) => {} + } + } + + // If there are no shared mounts to be set up, return directly. + if src_ctrs.is_empty() { + return Ok(()); + } + + let mounts = mounts.clone(); + let init_mntns = fcntl::open( + "/proc/self/ns/mnt", + OFlag::O_RDONLY | OFlag::O_CLOEXEC, + Mode::empty(), + ) + .map_err(|e| anyhow!("failed to open /proc/self/ns/mnt: {}", e))?; + // safe because the fd are opened by fcntl::open and used directly. + let _init_mntns_f = unsafe { fs::File::from_raw_fd(init_mntns) }; + let dst_mntns_path = format!("/proc/{}/ns/mnt", c.init_process_pid); + let dst_mntns = fcntl::open( + dst_mntns_path.as_str(), + OFlag::O_RDONLY | OFlag::O_CLOEXEC, + Mode::empty(), + ) + .map_err(|e| anyhow!("failed to open {}: {}", dst_mntns_path.as_str(), e))?; + // safe because the fd are opened by fcntl::open and used directly. + let _dst_mntns_f = unsafe { fs::File::from_raw_fd(dst_mntns) }; + let new_thread = std::thread::spawn(move || { + || -> Result<()> { + // A process can't join a new mount namespace if it is sharing + // filesystem-related attributes (using CLONE_FS flag) with another process. + // Ref: https://man7.org/linux/man-pages/man2/setns.2.html + // + // The implementation of the Rust standard library's std::thread relies on + // the CLONE_FS parameter at the low level. + // Therefore, it is not possible to switch directly to the mount namespace using setns. + // Instead, it is necessary to first switch to a new mount namespace using unshare. + unshare(CloneFlags::CLONE_NEWNS) + .map_err(|e| anyhow!("failed to create new mount namespace: {}", e))?; + for m in mounts { + if let Some(src_init_pid) = src_ctrs.get(m.src_ctr()) { + // Shared mount points are created by application process within the source container, + // so we need to ensure they are already prepared. + setns(init_mntns, CloneFlags::CLONE_NEWNS).map_err(|e| { + anyhow!("switch to initial mount namespace failed: {}", e) + })?; + let mut is_ready = false; + let start_time = Instant::now(); + let time_out = Duration::from_millis(10_000); + loop { + let proc_mounts_path = format!("/proc/{}/mounts", *src_init_pid); + let proc_mounts = fs::read_to_string(proc_mounts_path.as_str())?; + let lines: Vec<&str> = proc_mounts.split('\n').collect(); + for line in lines { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 2 && parts[1] == m.src_path() { + is_ready = true; + break; + } + } + + if is_ready { + break; + } + + if start_time.elapsed() >= time_out { + break; + } + + thread::sleep(Duration::from_millis(100)); + } + if !is_ready { + continue; + } + + // Switch to the src container to obtain shared mount points. + let src_mntns_path = format!("/proc/{}/ns/mnt", *src_init_pid); + let src_mntns = fcntl::open( + src_mntns_path.as_str(), + OFlag::O_RDONLY | OFlag::O_CLOEXEC, + Mode::empty(), + ) + .map_err(|e| { + anyhow!("failed to open {}: {}", src_mntns_path.as_str(), e) + })?; + // safe because the fd are opened by fcntl::open and used directly. + let _src_mntns_f = unsafe { fs::File::from_raw_fd(src_mntns) }; + setns(src_mntns, CloneFlags::CLONE_NEWNS).map_err(|e| { + anyhow!("switch to source mount namespace failed: {}", e) + })?; + let src = std::ffi::CString::new(m.src_path())?; + let mount_fd = unsafe { + syscall( + libc::SYS_open_tree, + libc::AT_FDCWD, + src.as_ptr(), + 0x1 | 0x8000 | libc::O_CLOEXEC, // OPEN_TREE_CLONE | AT_RECURSIVE | OPEN_TREE_CLOEXEC + ) as i32 + }; + if mount_fd < 0 { + return Err(anyhow!( + "failed to clone mounted subtree on {}", + m.src_path() + )); + } + // safe because we have checked whether mount_fd is valid + let _mount_f = unsafe { fs::File::from_raw_fd(mount_fd) }; + + // Switch to the dst container and mount them. + setns(dst_mntns, CloneFlags::CLONE_NEWNS).map_err(|e| { + anyhow!("switch to destination mount namespace failed: {}", e) + })?; + fs::create_dir_all(m.dst_path())?; + let dst = std::ffi::CString::new(m.dst_path())?; + let empty = std::ffi::CString::new("")?; + unsafe { + syscall( + libc::SYS_move_mount, + mount_fd, + empty.as_ptr(), + libc::AT_FDCWD, + dst.as_ptr(), + 4, // MOVE_MOUNT_F_EMPTY_PATH + ) + }; + } + } + + Ok(()) + }() + }); + + new_thread + .join() + .map_err(|e| anyhow!("Failed to join thread {:?}!", e))??; + + Ok(()) + } } #[instrument] @@ -683,6 +843,7 @@ mod tests { spec: Some(spec), rootless_euid: false, rootless_cgroup: false, + container_name: "".to_string(), } } diff --git a/src/libs/kata-types/src/annotations/cri_containerd.rs b/src/libs/kata-types/src/annotations/cri_containerd.rs index 8b2d63fafd..a6e54f2844 100644 --- a/src/libs/kata-types/src/annotations/cri_containerd.rs +++ b/src/libs/kata-types/src/annotations/cri_containerd.rs @@ -7,6 +7,7 @@ #![allow(missing_docs)] pub const CONTAINER_TYPE_LABEL_KEY: &str = "io.kubernetes.cri.container-type"; +pub const CONTAINER_NAME_LABEL_KEY: &str = "io.kubernetes.cri.container-name"; pub const SANDBOX: &str = "sandbox"; pub const CONTAINER: &str = "container"; diff --git a/src/libs/kata-types/src/annotations/crio.rs b/src/libs/kata-types/src/annotations/crio.rs index c8b2311f84..b0a618893d 100644 --- a/src/libs/kata-types/src/annotations/crio.rs +++ b/src/libs/kata-types/src/annotations/crio.rs @@ -6,7 +6,8 @@ #![allow(missing_docs)] -pub const CONTAINER_TYPE_LABEL_KEY: &str = "io.kubernetes.cri.container-type"; +pub const CONTAINER_TYPE_LABEL_KEY: &str = "io.kubernetes.cri-o.ContainerType"; +pub const CONTAINER_NAME_LABEL_KEY: &str = "io.kubernetes.cri-o.ContainerName"; pub const SANDBOX: &str = "sandbox"; pub const CONTAINER: &str = "container"; diff --git a/src/libs/kata-types/src/annotations/mod.rs b/src/libs/kata-types/src/annotations/mod.rs index 0b17b10628..a353d3e51b 100644 --- a/src/libs/kata-types/src/annotations/mod.rs +++ b/src/libs/kata-types/src/annotations/mod.rs @@ -310,6 +310,8 @@ pub const KATA_ANNO_CFG_DISABLE_NEW_NETNS: &str = "io.katacontainers.config.runtime.disable_new_netns"; /// A sandbox annotation to specify how attached VFIO devices should be treated. pub const KATA_ANNO_CFG_VFIO_MODE: &str = "io.katacontainers.config.runtime.vfio_mode"; +/// An annotation to declare shared mount points, which is a set of mount points that directly share mounted objects between containers. +pub const KATA_ANNO_CFG_SHARED_MOUNTS: &str = "io.katacontainers.config.runtime.shared_mounts"; /// A sandbox annotation used to specify prefetch_files.list host path container image /// being used, @@ -972,6 +974,9 @@ impl Annotation { KATA_ANNO_CFG_VFIO_MODE => { config.runtime.vfio_mode = value.to_string(); } + KATA_ANNO_CFG_SHARED_MOUNTS => { + config.runtime.shared_mounts = serde_json::from_str(value.as_str())?; + } KATA_ANNO_CFG_SANDBOX_BIND_MOUNTS => { let args: Vec = value .to_string() diff --git a/src/libs/kata-types/src/config/runtime.rs b/src/libs/kata-types/src/config/runtime.rs index 853e4aef3f..65e48f7bf6 100644 --- a/src/libs/kata-types/src/config/runtime.rs +++ b/src/libs/kata-types/src/config/runtime.rs @@ -11,6 +11,10 @@ use crate::config::{ConfigOps, TomlConfig}; use crate::mount::split_bind_mounts; use crate::{eother, validate_path}; +#[path = "shared_mount.rs"] +pub mod shared_mount; +pub use shared_mount::SharedMount; + /// Type of runtime VirtContainer. pub const RUNTIME_NAME_VIRTCONTAINER: &str = "virt_container"; @@ -148,6 +152,10 @@ pub struct Runtime { /// to the hypervisor. #[serde(default)] pub dan_conf: String, + + /// shared_mount declarations + #[serde(default)] + pub shared_mounts: Vec, } impl ConfigOps for Runtime { @@ -194,6 +202,10 @@ impl ConfigOps for Runtime { )); } + for shared_mount in &conf.runtime.shared_mounts { + shared_mount.validate()?; + } + for bind in conf.runtime.sandbox_bind_mounts.iter() { // Just validate the real_path. let (real_path, _mode) = split_bind_mounts(bind); diff --git a/src/libs/kata-types/src/config/shared_mount.rs b/src/libs/kata-types/src/config/shared_mount.rs new file mode 100644 index 0000000000..2370ba81aa --- /dev/null +++ b/src/libs/kata-types/src/config/shared_mount.rs @@ -0,0 +1,250 @@ +use std::io::Result; + +use regex::Regex; + +use crate::eother; + +#[derive(Debug, Deserialize, Serialize, Clone, Default)] +pub struct SharedMount { + /// Name is used to identify a pair of shared mount points. + /// This field cannot be omitted. + #[serde(default)] + pub name: String, + + /// Src_ctr is used to specify the name of the source container. + /// This field cannot be omitted. + #[serde(default)] + pub src_ctr: String, + + /// Src_path is used to specify the path to the shared mount point in the source container. + /// Src_path must conform to the regular expression `^(/[-\w.]+)+/?$` and cannot contain `/../`. + /// This field cannot be omitted. + #[serde(default)] + pub src_path: String, + + /// Dst_ctr is used to specify the name of the destination container. + /// This field cannot be omitted. + #[serde(default)] + pub dst_ctr: String, + + /// Dst_path is used to specify the destination path where the shared mount point will be mounted. + /// Dst_path must conform to the regular expression `^(/[-\w.]+)+/?$` and cannot contain `/../`. + /// This field cannot be omitted. + #[serde(default)] + pub dst_path: String, +} + +impl SharedMount { + pub fn validate(&self) -> Result<()> { + if self.name == "" { + return Err(eother!("shared_mount: field 'name' couldn't be empty.")); + } + if self.src_ctr == "" { + return Err(eother!("shared_mount: field 'src_ctr' couldn't be empty.")); + } + if self.dst_ctr == "" { + return Err(eother!("shared_mount: field 'dst_ctr' couldn't be empty.")); + } + if self.src_path == "" { + return Err(eother!("shared_mount: field 'src_path' couldn't be empty.")); + } + if self.dst_path == "" { + return Err(eother!("shared_mount: field 'dst_path' couldn't be empty.")); + } + + let re = match Regex::new(r"^(/[-\w.]+)+/?$") { + Ok(re) => re, + Err(e) => return Err(eother!("Compiling the regular expression failed: {}.", e)), + }; + if !re.is_match(&self.src_path) { + return Err(eother!("shared_mount '{}': src_path is invalid. It must be an absolute path and can only contain letters, numbers, hyphens(-), underscores(_) and dots(.).", self.name)); + } + let dirs: Vec<&str> = self.src_path.split('/').collect(); + for dir in dirs { + if dir == ".." { + return Err(eother!( + "shared_mount '{}': src_path couldn't contain '..' directory.", + self.name + )); + } + } + if !re.is_match(&self.dst_path) { + return Err(eother!("shared_mount '{}': dst_path is invalid. It must be an absolute path and can only contain letters, numbers, hyphens(-), underscores(_) and dots(.).", self.name)); + } + let dirs: Vec<&str> = self.dst_path.split('/').collect(); + for dir in dirs { + if dir == ".." { + return Err(eother!( + "shared_mount '{}': dst_path couldn't contain '..' directory.", + self.name + )); + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_validate() { + #[derive(Debug)] + struct TestData<'a> { + shared_mount_annotation: &'a str, + result: bool, + message: &'a str, + } + + let tests = &[ + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/mnt/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage" + }"#, + result: true, + message: "", + }, + TestData { + shared_mount_annotation: r#" + { + "src_ctr": "sidecar", + "src_path": "/mnt/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount: field 'name' couldn't be empty.", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src": "sidecar", + "src_path": "/mnt/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount: field 'src_ctr' couldn't be empty.", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_dir": "/mnt/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount: field 'src_path' couldn't be empty.", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/mnt/storage", + "dst_container": "app", + "dst_path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount: field 'dst_ctr' couldn't be empty.", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/mnt/storage", + "dst_ctr": "app", + "path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount: field 'dst_path' couldn't be empty.", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/_._/._/_/._", + "dst_ctr": "app", + "dst_path": "/-.-/.-/-/.-" + }"#, + result: true, + message: "", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "~/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount 'test': src_path is invalid. It must be an absolute path and can only contain letters, numbers, hyphens(-), underscores(_) and dots(.).", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/mnt/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage|ls" + }"#, + result: false, + message: "shared_mount 'test': dst_path is invalid. It must be an absolute path and can only contain letters, numbers, hyphens(-), underscores(_) and dots(.).", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/../mnt/storage", + "dst_ctr": "app", + "dst_path": "/mnt/storage" + }"#, + result: false, + message: "shared_mount 'test': src_path couldn't contain '..' directory.", + }, + TestData { + shared_mount_annotation: r#" + { + "name": "test", + "src_ctr": "sidecar", + "src_path": "/mnt/storage", + "dst_ctr": "app", + "dst_path": "/../mnt/storage" + }"#, + result: false, + message: "shared_mount 'test': dst_path couldn't contain '..' directory.", + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + + let m: SharedMount = serde_json::from_str(d.shared_mount_annotation).unwrap(); + let result = m.validate(); + + let msg = format!("{}, result: {:?}", msg, result); + + assert_eq!(result.is_ok(), d.result, "{}", msg); + + if !d.result { + assert_eq!(result.unwrap_err().to_string(), d.message, "{}", msg); + } + } + } +} diff --git a/src/libs/kata-types/src/k8s.rs b/src/libs/kata-types/src/k8s.rs index 2b5971ccc1..d49c506162 100644 --- a/src/libs/kata-types/src/k8s.rs +++ b/src/libs/kata-types/src/k8s.rs @@ -72,6 +72,22 @@ pub fn container_type(spec: &oci::Spec) -> ContainerType { ContainerType::SingleContainer } +/// Get K8S container name from OCI annotations. +pub fn container_name(spec: &oci::Spec) -> String { + for k in [ + annotations::cri_containerd::CONTAINER_NAME_LABEL_KEY, + annotations::crio::CONTAINER_NAME_LABEL_KEY, + ] + .iter() + { + if let Some(v) = spec.annotations.get(k.to_owned()) { + return v.clone(); + } + } + + String::new() +} + /// Determine the k8s sandbox ID from OCI annotations. /// /// This function is expected to be called only when the container type is "PodContainer". diff --git a/src/libs/protocols/protos/agent.proto b/src/libs/protocols/protos/agent.proto index 039630d306..bee72f0667 100644 --- a/src/libs/protocols/protos/agent.proto +++ b/src/libs/protocols/protos/agent.proto @@ -89,6 +89,10 @@ message CreateContainerRequest { // The agent would receive an OCI spec with PID namespace cleared // out altogether and not just the pid ns path. bool sandbox_pidns = 7; + + // This field is used to declare a set of shared mount points + // that support cross-container sharing of mount objects. + repeated SharedMount shared_mounts = 8; } message StartContainerRequest { @@ -444,6 +448,23 @@ message FSGroup { types.FSGroupChangePolicy group_change_policy = 3; } +// SharedMount declares a set of shared mount points that support +// cross-container sharing of mount objects. +message SharedMount { + // Name is used to identify a pair of shared mount points. + string name = 1; + // Src_ctr is used to specify the name of the source container. + string src_ctr = 2; + // Src_path is used to specify the path of the mount point. If the path doesn't + // exist in the rootfs, it will be created. + string src_path = 3; + // Dst_ctr is used to specify the name of the destination container. + string dst_ctr = 4; + // Dst_path is used to specify the path of the mount point. If the path doesn't + // exist in the rootfs, it will be created. + string dst_path = 5; +} + // Storage represents both the rootfs of the container, and any volume that // could have been defined through the Mount list of the OCI specification. message Storage { diff --git a/src/runtime-rs/crates/agent/src/kata/trans.rs b/src/runtime-rs/crates/agent/src/kata/trans.rs index 55635c6c59..2190806822 100644 --- a/src/runtime-rs/crates/agent/src/kata/trans.rs +++ b/src/runtime-rs/crates/agent/src/kata/trans.rs @@ -22,10 +22,10 @@ use crate::{ MemoryStats, MetricsResponse, NetworkStats, OnlineCPUMemRequest, PidsStats, ReadStreamRequest, ReadStreamResponse, RemoveContainerRequest, ReseedRandomDevRequest, ResizeVolumeRequest, Route, Routes, SetGuestDateTimeRequest, SetIPTablesRequest, - SetIPTablesResponse, SignalProcessRequest, StatsContainerResponse, Storage, StringUser, - ThrottlingData, TtyWinResizeRequest, UpdateContainerRequest, UpdateInterfaceRequest, - UpdateRoutesRequest, VersionCheckResponse, VolumeStatsRequest, VolumeStatsResponse, - WaitProcessRequest, WriteStreamRequest, + SetIPTablesResponse, SharedMount, SignalProcessRequest, StatsContainerResponse, Storage, + StringUser, ThrottlingData, TtyWinResizeRequest, UpdateContainerRequest, + UpdateInterfaceRequest, UpdateRoutesRequest, VersionCheckResponse, VolumeStatsRequest, + VolumeStatsResponse, WaitProcessRequest, WriteStreamRequest, }, OomEventResponse, WaitProcessResponse, WriteStreamResponse, }; @@ -117,6 +117,19 @@ impl From for agent::Storage { } } +impl From for agent::SharedMount { + fn from(from: SharedMount) -> Self { + Self { + name: from.name, + src_ctr: from.src_ctr, + src_path: from.src_path, + dst_ctr: from.dst_ctr, + dst_path: from.dst_path, + ..Default::default() + } + } +} + impl From for agent::KernelModule { fn from(from: KernelModule) -> Self { Self { @@ -260,6 +273,7 @@ impl From for agent::CreateContainerRequest { storages: trans_vec(from.storages), OCI: from_option(from.oci), sandbox_pidns: from.sandbox_pidns, + shared_mounts: trans_vec(from.shared_mounts), ..Default::default() } } diff --git a/src/runtime-rs/crates/agent/src/types.rs b/src/runtime-rs/crates/agent/src/types.rs index 325d84588d..7cb3cfd445 100644 --- a/src/runtime-rs/crates/agent/src/types.rs +++ b/src/runtime-rs/crates/agent/src/types.rs @@ -60,6 +60,15 @@ pub struct Storage { pub mount_point: String, } +#[derive(PartialEq, Clone, Default)] +pub struct SharedMount { + pub name: String, + pub src_ctr: String, + pub src_path: String, + pub dst_ctr: String, + pub dst_path: String, +} + #[derive(Deserialize, Default, Clone, PartialEq, Eq, Debug, Hash)] pub enum IPFamily { #[default] @@ -118,6 +127,7 @@ pub struct CreateContainerRequest { pub oci: Option, pub sandbox_pidns: bool, pub rootfs_mounts: Vec, + pub shared_mounts: Vec, } #[derive(PartialEq, Clone, Default)] diff --git a/src/runtime-rs/crates/runtimes/virt_container/src/container_manager/container.rs b/src/runtime-rs/crates/runtimes/virt_container/src/container_manager/container.rs index efab56a682..7bf7b33720 100644 --- a/src/runtime-rs/crates/runtimes/virt_container/src/container_manager/container.rs +++ b/src/runtime-rs/crates/runtimes/virt_container/src/container_manager/container.rs @@ -17,6 +17,7 @@ use common::{ }, }; use kata_sys_util::k8s::update_ephemeral_storage_type; +use kata_types::k8s; use oci::{LinuxResources, Process as OCIProcess}; use resource::{ResourceManager, ResourceUpdateOp}; @@ -168,6 +169,21 @@ impl Container { linux.resources = resources; } + let container_name = k8s::container_name(&spec); + let mut shared_mounts = Vec::new(); + for shared_mount in &toml_config.runtime.shared_mounts { + if shared_mount.dst_ctr == container_name { + let m = agent::types::SharedMount { + name: shared_mount.name.clone(), + src_ctr: shared_mount.src_ctr.clone(), + src_path: shared_mount.src_path.clone(), + dst_ctr: shared_mount.dst_ctr.clone(), + dst_path: shared_mount.dst_path.clone(), + }; + shared_mounts.push(m); + } + } + // create container let r = agent::CreateContainerRequest { process_id: agent::ContainerProcessID::new(&config.container_id, ""), @@ -175,6 +191,7 @@ impl Container { oci: Some(spec), sandbox_pidns, devices: devices_agent, + shared_mounts, ..Default::default() }; diff --git a/src/tools/runk/Cargo.lock b/src/tools/runk/Cargo.lock index 3c377bb8d9..17ed6ae3eb 100644 --- a/src/tools/runk/Cargo.lock +++ b/src/tools/runk/Cargo.lock @@ -433,8 +433,18 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.12.4", + "darling_macro 0.12.4", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", ] [[package]] @@ -451,13 +461,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" dependencies = [ - "darling_core", + "darling_core 0.12.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", "quote", "syn 1.0.109", ] @@ -499,7 +533,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" dependencies = [ - "darling", + "darling 0.12.4", "proc-macro2", "quote", "syn 1.0.109", @@ -922,6 +956,7 @@ dependencies = [ "oci", "once_cell", "rand", + "safe-path", "serde_json", "slog", "slog-scope", @@ -944,6 +979,7 @@ dependencies = [ "regex", "safe-path", "serde", + "serde-enum-str", "serde_json", "slog", "slog-scope", @@ -1730,6 +1766,36 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-attributes" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eb8ec7724e4e524b2492b510e66957fe1a2c76c26a6975ec80823f2439da685" +dependencies = [ + "darling_core 0.14.4", + "serde-rename-rule", + "syn 1.0.109", +] + +[[package]] +name = "serde-enum-str" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26416dc95fcd46b0e4b12a3758043a229a6914050aaec2e8191949753ed4e9aa" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "serde-attributes", + "syn 1.0.109", +] + +[[package]] +name = "serde-rename-rule" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794e44574226fc701e3be5c651feb7939038fc67fb73f6f4dd5c4ba90fd3be70" + [[package]] name = "serde_derive" version = "1.0.136" diff --git a/src/tools/runk/libcontainer/src/init_builder.rs b/src/tools/runk/libcontainer/src/init_builder.rs index 6398e4363c..d3d106ec77 100644 --- a/src/tools/runk/libcontainer/src/init_builder.rs +++ b/src/tools/runk/libcontainer/src/init_builder.rs @@ -69,6 +69,7 @@ impl InitContainer { spec: Some(spec), rootless_euid: false, rootless_cgroup: false, + container_name: "".to_string(), }; debug!(logger, "create LinuxContainer with config: {:?}", config); let container = diff --git a/src/tools/runk/libcontainer/src/utils.rs b/src/tools/runk/libcontainer/src/utils.rs index ec1a71254a..0c602f0fd7 100644 --- a/src/tools/runk/libcontainer/src/utils.rs +++ b/src/tools/runk/libcontainer/src/utils.rs @@ -171,6 +171,7 @@ pub(crate) mod test_utils { spec: Some(spec), rootless_euid: false, rootless_cgroup: false, + container_name: "".to_string(), } }