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:
HanZiyao 2023-10-12 18:09:46 +08:00
parent 4c3a664358
commit a3b003c345
19 changed files with 598 additions and 11 deletions

1
src/agent/Cargo.lock generated
View File

@ -1202,6 +1202,7 @@ dependencies = [
"oci",
"once_cell",
"rand",
"safe-path",
"serde_json",
"slog",
"slog-scope",

View File

@ -1733,6 +1733,7 @@ mod tests {
spec: Some(spec),
rootless_euid: false,
rootless_cgroup: false,
container_name: "".to_string(),
}
}

View File

@ -14,4 +14,5 @@ pub struct CreateOpts {
pub spec: Option<Spec>,
pub rootless_euid: bool,
pub rootless_cgroup: bool,
pub container_name: String,
}

View File

@ -563,6 +563,7 @@ mod tests {
rootless_euid: false,
rootless_cgroup: false,
spec: Some(spec),
container_name: "container1".to_owned(),
};
validate(&config).unwrap_err();

View File

@ -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(),
}
}

View File

@ -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(),
}
}

View File

@ -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";

View File

@ -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";

View File

@ -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()

View File

@ -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);

View 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);
}
}
}
}

View File

@ -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".

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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)]

View File

@ -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()
};

View File

@ -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"

View File

@ -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 =

View File

@ -171,6 +171,7 @@ pub(crate) mod test_utils {
spec: Some(spec),
rootless_euid: false,
rootless_cgroup: false,
container_name: "".to_string(),
}
}