mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-27 07:48:55 +00:00
agent: support bind mounts between containers
This feature supports creating bind mounts directly between containers through annotations. Fixes: #6715 Signed-off-by: HanZiyao <h56983577@126.com>
This commit is contained in:
parent
4c3a664358
commit
a3b003c345
1
src/agent/Cargo.lock
generated
1
src/agent/Cargo.lock
generated
@ -1202,6 +1202,7 @@ dependencies = [
|
||||
"oci",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"safe-path",
|
||||
"serde_json",
|
||||
"slog",
|
||||
"slog-scope",
|
||||
|
@ -1733,6 +1733,7 @@ mod tests {
|
||||
spec: Some(spec),
|
||||
rootless_euid: false,
|
||||
rootless_cgroup: false,
|
||||
container_name: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,4 +14,5 @@ pub struct CreateOpts {
|
||||
pub spec: Option<Spec>,
|
||||
pub rootless_euid: bool,
|
||||
pub rootless_cgroup: bool,
|
||||
pub container_name: String,
|
||||
}
|
||||
|
@ -563,6 +563,7 @@ mod tests {
|
||||
rootless_euid: false,
|
||||
rootless_cgroup: false,
|
||||
spec: Some(spec),
|
||||
container_name: "container1".to_owned(),
|
||||
};
|
||||
|
||||
validate(&config).unwrap_err();
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<SharedMount>) -> Result<()> {
|
||||
let mut src_ctrs: HashMap<String, i32> = 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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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<String> = value
|
||||
.to_string()
|
||||
|
@ -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<SharedMount>,
|
||||
}
|
||||
|
||||
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);
|
||||
|
250
src/libs/kata-types/src/config/shared_mount.rs
Normal file
250
src/libs/kata-types/src/config/shared_mount.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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".
|
||||
|
@ -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 {
|
||||
|
@ -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<Storage> for agent::Storage {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedMount> 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<KernelModule> for agent::KernelModule {
|
||||
fn from(from: KernelModule) -> Self {
|
||||
Self {
|
||||
@ -260,6 +273,7 @@ impl From<CreateContainerRequest> 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()
|
||||
}
|
||||
}
|
||||
|
@ -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<oci::Spec>,
|
||||
pub sandbox_pidns: bool,
|
||||
pub rootfs_mounts: Vec<oci::Mount>,
|
||||
pub shared_mounts: Vec<SharedMount>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Default)]
|
||||
|
@ -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()
|
||||
};
|
||||
|
||||
|
74
src/tools/runk/Cargo.lock
generated
74
src/tools/runk/Cargo.lock
generated
@ -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"
|
||||
|
@ -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 =
|
||||
|
@ -171,6 +171,7 @@ pub(crate) mod test_utils {
|
||||
spec: Some(spec),
|
||||
rootless_euid: false,
|
||||
rootless_cgroup: false,
|
||||
container_name: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user