kata-sys-utils: align OCI Spec with oci-spec-rs

Do align oci spec and fix warnings to make clippy
happy.

Fixes #9766

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
This commit is contained in:
Alex Lyn 2024-07-16 15:54:19 +08:00
parent 012029063c
commit efb7390357
8 changed files with 167 additions and 81 deletions

View File

@ -31,7 +31,8 @@ thiserror = "1.0.30"
hex = "0.4.3" hex = "0.4.3"
kata-types = { path = "../kata-types" } kata-types = { path = "../kata-types" }
oci = { path = "../oci" } oci-spec = { version = "0.6.8", features = ["runtime"] }
runtime-spec = { path = "../runtime-spec" }
safe-path = { path = "../safe-path" } safe-path = { path = "../safe-path" }
[dev-dependencies] [dev-dependencies]

View File

@ -54,7 +54,7 @@ pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> {
} }
if cpu_flags_tag.is_empty() { if cpu_flags_tag.is_empty() {
return Err(anyhow!("cpu flags delimiter string is empty"))?; return Err(anyhow!("cpu flags delimiter string is empty"));
} }
get_cpu_flags_from_file(cpu_info, cpu_flags_tag) get_cpu_flags_from_file(cpu_info, cpu_flags_tag)
@ -71,7 +71,7 @@ pub fn get_cpu_flags_vec(cpu_info: &str, cpu_flags_tag: &str) -> Result<Vec<Stri
} }
if cpu_flags_tag.is_empty() { if cpu_flags_tag.is_empty() {
return Err(anyhow!("cpu flags delimiter string is empty"))?; return Err(anyhow!("cpu flags delimiter string is empty"));
} }
let flags = get_cpu_flags_from_file(cpu_info, cpu_flags_tag)?; let flags = get_cpu_flags_from_file(cpu_info, cpu_flags_tag)?;
@ -93,7 +93,7 @@ pub fn get_cpu_flags_vec(cpu_info: &str, cpu_flags_tag: &str) -> Result<Vec<Stri
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] #[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
pub fn contains_cpu_flag(flags_vec: &[String], flag: &str) -> Result<bool> { pub fn contains_cpu_flag(flags_vec: &[String], flag: &str) -> Result<bool> {
if flag.is_empty() { if flag.is_empty() {
return Err(anyhow!("parameter specifying flag to look for is empty"))?; return Err(anyhow!("parameter specifying flag to look for is empty"));
} }
Ok(flags_vec.iter().any(|f| f == flag)) Ok(flags_vec.iter().any(|f| f == flag))

View File

@ -8,9 +8,9 @@ use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::io::{self, Read, Result}; use std::io::{self, Read, Result};
use std::path::Path;
use std::time::Duration; use std::time::Duration;
use oci_spec::runtime as oci;
use subprocess::{ExitStatus, Popen, PopenConfig, PopenError, Redirection}; use subprocess::{ExitStatus, Popen, PopenConfig, PopenError, Redirection};
use crate::validate::valid_env; use crate::validate::valid_env;
@ -40,10 +40,10 @@ impl Eq for HookKey {}
impl Hash for HookKey { impl Hash for HookKey {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.path.hash(state); self.0.path().hash(state);
self.0.args.hash(state); self.0.args().hash(state);
self.0.env.hash(state); self.0.env().hash(state);
self.0.timeout.hash(state); self.0.timeout().hash(state);
} }
} }
@ -114,7 +114,11 @@ impl HookStates {
/// ///
/// The OCI spec also defines the context to invoke hooks, caller needs to take the responsibility /// The OCI spec also defines the context to invoke hooks, caller needs to take the responsibility
/// to setup execution context, such as namespace etc. /// to setup execution context, such as namespace etc.
pub fn execute_hook(&mut self, hook: &oci::Hook, state: Option<oci::State>) -> Result<()> { pub fn execute_hook(
&mut self,
hook: &oci::Hook,
state: Option<runtime_spec::State>,
) -> Result<()> {
if self.get(hook) != HookState::Pending { if self.get(hook) != HookState::Pending {
return Ok(()); return Ok(());
} }
@ -150,7 +154,7 @@ impl HookStates {
executor.execute_with_input(&mut popen, state)?; executor.execute_with_input(&mut popen, state)?;
} }
executor.execute_and_wait(&mut popen)?; executor.execute_and_wait(&mut popen)?;
info!(sl!(), "hook {} finished", hook.path); info!(sl!(), "hook {} finished", hook.path().display());
self.states.insert(hook.into(), HookState::Done); self.states.insert(hook.into(), HookState::Done);
Ok(()) Ok(())
@ -165,11 +169,15 @@ impl HookStates {
/// ///
/// The execution result will be recorded for each hook. Once a hook returns success, it will not /// The execution result will be recorded for each hook. Once a hook returns success, it will not
/// be invoked anymore. /// be invoked anymore.
pub fn execute_hooks(&mut self, hooks: &[oci::Hook], state: Option<oci::State>) -> Result<()> { pub fn execute_hooks(
&mut self,
hooks: &[oci::Hook],
state: Option<runtime_spec::State>,
) -> Result<()> {
for hook in hooks.iter() { for hook in hooks.iter() {
if let Err(e) = self.execute_hook(hook, state.clone()) { if let Err(e) = self.execute_hook(hook, state.clone()) {
// Ignore error and try next hook, the caller should retry. // Ignore error and try next hook, the caller should retry.
error!(sl!(), "hook {} failed: {}", hook.path, e); error!(sl!(), "hook {} failed: {}", hook.path().display(), e);
} }
} }
@ -188,10 +196,10 @@ struct HookExecutor<'a> {
impl<'a> HookExecutor<'a> { impl<'a> HookExecutor<'a> {
fn new(hook: &'a oci::Hook) -> Result<Self> { fn new(hook: &'a oci::Hook) -> Result<Self> {
// Ensure Hook.path is present and is an absolute path. // Ensure Hook.path is present and is an absolute path.
let executable = if hook.path.is_empty() { let executable = if !hook.path().exists() {
return Err(eother!("path of hook {:?} is empty", hook)); return Err(eother!("path of hook {:?} is empty", hook));
} else { } else {
let path = Path::new(&hook.path); let path = hook.path();
if !path.is_absolute() { if !path.is_absolute() {
return Err(eother!("path of hook {:?} is not absolute", hook)); return Err(eother!("path of hook {:?} is not absolute", hook));
} }
@ -199,22 +207,23 @@ impl<'a> HookExecutor<'a> {
}; };
// Hook.args is optional, use Hook.path as arg0 if Hook.args is empty. // Hook.args is optional, use Hook.path as arg0 if Hook.args is empty.
let args = if hook.args.is_empty() { let args = match hook.args() {
vec![hook.path.clone()] Some(args) => args.clone(),
} else { None => vec![hook.path().display().to_string()],
hook.args.clone()
}; };
let mut envs: Vec<(OsString, OsString)> = Vec::new(); let mut envs: Vec<(OsString, OsString)> = Vec::new();
for e in hook.env.iter() { if let Some(env) = hook.env() {
if let Some((key, value)) = valid_env(e) { for e in env.iter() {
envs.push((OsString::from(key), OsString::from(value))); if let Some((key, value)) = valid_env(e) {
envs.push((OsString::from(key), OsString::from(value)));
}
} }
} }
// Use Hook.timeout if it's valid, otherwise default to 10s. // Use Hook.timeout if it's valid, otherwise default to 10s.
let mut timeout = DEFAULT_HOOK_TIMEOUT_SEC as u64; let mut timeout = DEFAULT_HOOK_TIMEOUT_SEC as u64;
if let Some(t) = hook.timeout { if let Some(t) = hook.timeout() {
if t > 0 { if t > 0 {
timeout = t as u64; timeout = t as u64;
} }
@ -229,7 +238,7 @@ impl<'a> HookExecutor<'a> {
}) })
} }
fn execute_with_input(&mut self, popen: &mut Popen, state: oci::State) -> Result<()> { fn execute_with_input(&mut self, popen: &mut Popen, state: runtime_spec::State) -> Result<()> {
let st = serde_json::to_string(&state)?; let st = serde_json::to_string(&state)?;
let (stdout, stderr) = popen let (stdout, stderr) = popen
.communicate_start(Some(st.as_bytes().to_vec())) .communicate_start(Some(st.as_bytes().to_vec()))
@ -238,12 +247,22 @@ impl<'a> HookExecutor<'a> {
.map_err(|e| e.error)?; .map_err(|e| e.error)?;
if let Some(err) = stderr { if let Some(err) = stderr {
if !err.is_empty() { if !err.is_empty() {
error!(sl!(), "hook {} exec failed: {}", self.hook.path, err); error!(
sl!(),
"hook {} exec failed: {}",
self.hook.path().display(),
err
);
} }
} }
if let Some(out) = stdout { if let Some(out) = stdout {
if !out.is_empty() { if !out.is_empty() {
info!(sl!(), "hook {} exec stdout: {}", self.hook.path, out); info!(
sl!(),
"hook {} exec stdout: {}",
self.hook.path().display(),
out
);
} }
} }
// Give a grace period for `execute_and_wait()`. // Give a grace period for `execute_and_wait()`.
@ -333,6 +352,7 @@ mod tests {
use std::fs::{self, set_permissions, File, Permissions}; use std::fs::{self, set_permissions, File, Permissions};
use std::io::Write; use std::io::Write;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
fn test_hook_eq(hook1: &oci::Hook, hook2: &oci::Hook, expected: bool) { fn test_hook_eq(hook1: &oci::Hook, hook2: &oci::Hook, expected: bool) {
@ -341,17 +361,41 @@ mod tests {
assert_eq!(key1 == key2, expected); assert_eq!(key1 == key2, expected);
} }
// struct Hook is just for test cases
#[derive(Clone)]
pub struct Hook {
pub path: String,
pub args: Vec<String>,
pub env: Vec<String>,
pub timeout: Option<i64>,
}
impl Hook {
fn build_oci_hook(self) -> oci::Hook {
let mut hook = oci::Hook::default();
hook.set_path(PathBuf::from(self.path));
hook.set_args(Some(self.args));
hook.set_env(Some(self.env));
hook.set_timeout(self.timeout);
hook
}
}
#[test] #[test]
fn test_hook_key() { fn test_hook_key() {
let hook = oci::Hook { let hook = Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string(), "3".to_string()], args: vec!["2".to_string(), "3".to_string()],
env: vec![], env: vec![],
timeout: Some(0), timeout: Some(0),
}; };
let oci_hook = hook.build_oci_hook();
let cases = [ let cases = [
( (
oci::Hook { Hook {
path: "1000".to_string(), path: "1000".to_string(),
args: vec!["2".to_string(), "3".to_string()], args: vec!["2".to_string(), "3".to_string()],
env: vec![], env: vec![],
@ -360,7 +404,7 @@ mod tests {
false, false,
), ),
( (
oci::Hook { Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string(), "4".to_string()], args: vec!["2".to_string(), "4".to_string()],
env: vec![], env: vec![],
@ -369,7 +413,7 @@ mod tests {
false, false,
), ),
( (
oci::Hook { Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string()], args: vec!["2".to_string()],
env: vec![], env: vec![],
@ -378,7 +422,7 @@ mod tests {
false, false,
), ),
( (
oci::Hook { Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string(), "3".to_string()], args: vec!["2".to_string(), "3".to_string()],
env: vec!["5".to_string()], env: vec!["5".to_string()],
@ -387,7 +431,7 @@ mod tests {
false, false,
), ),
( (
oci::Hook { Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string(), "3".to_string()], args: vec!["2".to_string(), "3".to_string()],
env: vec![], env: vec![],
@ -396,7 +440,7 @@ mod tests {
false, false,
), ),
( (
oci::Hook { Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string(), "3".to_string()], args: vec!["2".to_string(), "3".to_string()],
env: vec![], env: vec![],
@ -405,7 +449,7 @@ mod tests {
false, false,
), ),
( (
oci::Hook { Hook {
path: "1".to_string(), path: "1".to_string(),
args: vec!["2".to_string(), "3".to_string()], args: vec!["2".to_string(), "3".to_string()],
env: vec![], env: vec![],
@ -416,7 +460,8 @@ mod tests {
]; ];
for case in cases.iter() { for case in cases.iter() {
test_hook_eq(&hook, &case.0, case.1); let case_hook = case.0.clone().build_oci_hook();
test_hook_eq(&oci_hook, &case_hook, case.1);
} }
} }
@ -435,50 +480,53 @@ mod tests {
// test case 1: normal // test case 1: normal
// execute hook // execute hook
let hook = oci::Hook { let hook = Hook {
path: "/bin/touch".to_string(), path: "/bin/touch".to_string(),
args: vec!["touch".to_string(), file_str.to_string()], args: vec!["touch".to_string(), file_str.to_string()],
env: vec![], env: vec![],
timeout: Some(0), timeout: Some(0),
}; };
let ret = states.execute_hook(&hook, None); let oci_hook = hook.build_oci_hook();
let ret = states.execute_hook(&oci_hook, None);
assert!(ret.is_ok()); assert!(ret.is_ok());
assert!(fs::metadata(&file).is_ok()); assert!(fs::metadata(&file).is_ok());
assert!(!states.should_retry()); assert!(!states.should_retry());
// test case 2: timeout in 10s // test case 2: timeout in 10s
let hook = oci::Hook { let hook = Hook {
path: "/bin/sleep".to_string(), path: "/bin/sleep".to_string(),
args: vec!["sleep".to_string(), "3600".to_string()], args: vec!["sleep".to_string(), "3600".to_string()],
env: vec![], env: vec![],
timeout: Some(0), // default timeout is 10 seconds timeout: Some(0), // default timeout is 10 seconds
}; };
let oci_hook = hook.build_oci_hook();
let start = Instant::now(); let start = Instant::now();
let ret = states.execute_hook(&hook, None).unwrap_err(); let ret = states.execute_hook(&oci_hook, None).unwrap_err();
let duration = start.elapsed(); let duration = start.elapsed();
let used = duration.as_secs(); let used = duration.as_secs();
assert!((10..12u64).contains(&used)); assert!((10..12u64).contains(&used));
assert_eq!(ret.kind(), io::ErrorKind::TimedOut); assert_eq!(ret.kind(), io::ErrorKind::TimedOut);
assert_eq!(states.get(&hook), HookState::Pending); assert_eq!(states.get(&oci_hook), HookState::Pending);
assert!(states.should_retry()); assert!(states.should_retry());
states.remove(&hook); states.remove(&oci_hook);
// test case 3: timeout in 5s // test case 3: timeout in 5s
let hook = oci::Hook { let hook = Hook {
path: "/bin/sleep".to_string(), path: "/bin/sleep".to_string(),
args: vec!["sleep".to_string(), "3600".to_string()], args: vec!["sleep".to_string(), "3600".to_string()],
env: vec![], env: vec![],
timeout: Some(5), // timeout is set to 5 seconds timeout: Some(5), // timeout is set to 5 seconds
}; };
let oci_hook = hook.build_oci_hook();
let start = Instant::now(); let start = Instant::now();
let ret = states.execute_hook(&hook, None).unwrap_err(); let ret = states.execute_hook(&oci_hook, None).unwrap_err();
let duration = start.elapsed(); let duration = start.elapsed();
let used = duration.as_secs(); let used = duration.as_secs();
assert!((5..7u64).contains(&used)); assert!((5..7u64).contains(&used));
assert_eq!(ret.kind(), io::ErrorKind::TimedOut); assert_eq!(ret.kind(), io::ErrorKind::TimedOut);
assert_eq!(states.get(&hook), HookState::Pending); assert_eq!(states.get(&oci_hook), HookState::Pending);
assert!(states.should_retry()); assert!(states.should_retry());
states.remove(&hook); states.remove(&oci_hook);
// test case 4: with envs // test case 4: with envs
let create_shell = |shell_path: &str, data_path: &str| -> Result<()> { let create_shell = |shell_path: &str, data_path: &str| -> Result<()> {
@ -500,13 +548,14 @@ echo -n "K1=${{K1}}" > {}
let shell_path = format!("{}/test.sh", tmpdir.path().to_string_lossy()); let shell_path = format!("{}/test.sh", tmpdir.path().to_string_lossy());
let ret = create_shell(&shell_path, file_str.as_ref()); let ret = create_shell(&shell_path, file_str.as_ref());
assert!(ret.is_ok()); assert!(ret.is_ok());
let hook = oci::Hook { let hook = Hook {
path: shell_path, path: shell_path,
args: vec![], args: vec![],
env: vec!["K1=V1".to_string()], env: vec!["K1=V1".to_string()],
timeout: Some(5), timeout: Some(5),
}; };
let ret = states.execute_hook(&hook, None); let oci_hook = hook.build_oci_hook();
let ret = states.execute_hook(&oci_hook, None);
assert!(ret.is_ok()); assert!(ret.is_ok());
assert!(!states.should_retry()); assert!(!states.should_retry());
let contents = fs::read_to_string(file); let contents = fs::read_to_string(file);
@ -516,22 +565,23 @@ echo -n "K1=${{K1}}" > {}
} }
// test case 5: timeout in 5s with state // test case 5: timeout in 5s with state
let hook = oci::Hook { let hook = Hook {
path: "/bin/sleep".to_string(), path: "/bin/sleep".to_string(),
args: vec!["sleep".to_string(), "3600".to_string()], args: vec!["sleep".to_string(), "3600".to_string()],
env: vec![], env: vec![],
timeout: Some(6), // timeout is set to 5 seconds timeout: Some(6), // timeout is set to 5 seconds
}; };
let state = oci::State { let oci_hook = hook.build_oci_hook();
let state = runtime_spec::State {
version: "".to_string(), version: "".to_string(),
id: "".to_string(), id: "".to_string(),
status: oci::ContainerState::Creating, status: runtime_spec::ContainerState::Creating,
pid: 10, pid: 10,
bundle: "nouse".to_string(), bundle: "nouse".to_string(),
annotations: Default::default(), annotations: Default::default(),
}; };
let start = Instant::now(); let start = Instant::now();
let ret = states.execute_hook(&hook, Some(state)).unwrap_err(); let ret = states.execute_hook(&oci_hook, Some(state)).unwrap_err();
let duration = start.elapsed(); let duration = start.elapsed();
let used = duration.as_secs(); let used = duration.as_secs();
assert!((6..8u64).contains(&used)); assert!((6..8u64).contains(&used));

View File

@ -10,7 +10,7 @@
//! to detect K8S EmptyDir medium type from `oci::spec::Mount` objects. //! to detect K8S EmptyDir medium type from `oci::spec::Mount` objects.
use kata_types::mount; use kata_types::mount;
use oci::Spec; use oci_spec::runtime::Spec;
use crate::mount::get_linux_mount_info; use crate::mount::get_linux_mount_info;
@ -55,17 +55,24 @@ pub fn is_host_empty_dir(path: &str) -> bool {
// backed by tmpfs inside the VM. For successive containers // backed by tmpfs inside the VM. For successive containers
// of the same pod the already existing volume is reused. // of the same pod the already existing volume is reused.
pub fn update_ephemeral_storage_type(oci_spec: &mut Spec) { pub fn update_ephemeral_storage_type(oci_spec: &mut Spec) {
for m in oci_spec.mounts.iter_mut() { if let Some(mounts) = oci_spec.mounts_mut() {
if mount::is_kata_guest_mount_volume(&m.r#type) { for m in mounts.iter_mut() {
continue; if let Some(typ) = &m.typ() {
} if mount::is_kata_guest_mount_volume(typ) {
continue;
}
}
if is_ephemeral_volume(&m.source) { if let Some(source) = &m.source() {
m.r#type = String::from(mount::KATA_EPHEMERAL_VOLUME_TYPE); let mnt_src = &source.display().to_string();
} else if is_host_empty_dir(&m.source) { if is_ephemeral_volume(mnt_src) {
// FIXME support disable_guest_empty_dir m.set_typ(Some(String::from(mount::KATA_EPHEMERAL_VOLUME_TYPE)));
// https://github.com/kata-containers/kata-containers/blob/02a51e75a7e0c6fce5e8abe3b991eeac87e09645/src/runtime/pkg/katautils/create.go#L105 } else if is_host_empty_dir(mnt_src) {
m.r#type = String::from(mount::KATA_HOST_DIR_VOLUME_TYPE); // FIXME support disable_guest_empty_dir
// https://github.com/kata-containers/kata-containers/blob/02a51e75a7e0c6fce5e8abe3b991eeac87e09645/src/runtime/pkg/katautils/create.go#L105
m.set_typ(Some(String::from(mount::KATA_HOST_DIR_VOLUME_TYPE)));
}
}
} }
} }
} }

View File

@ -134,9 +134,10 @@ pub struct LinuxMountInfo {
/// Get the device and file system type of a mount point by parsing `/proc/mounts`. /// Get the device and file system type of a mount point by parsing `/proc/mounts`.
pub fn get_linux_mount_info(mount_point: &str) -> Result<LinuxMountInfo> { pub fn get_linux_mount_info(mount_point: &str) -> Result<LinuxMountInfo> {
let mount_file = fs::File::open(PROC_MOUNTS_FILE)?; let mount_file = fs::File::open(PROC_MOUNTS_FILE)?;
let lines = io::BufReader::new(mount_file).lines(); let reader = io::BufReader::new(mount_file);
for mount in lines.flatten() { for line in reader.lines() {
let mount = line?;
let fields: Vec<&str> = mount.split(' ').collect(); let fields: Vec<&str> = mount.split(' ').collect();
if fields.len() != PROC_FIELDS_PER_LINE { if fields.len() != PROC_FIELDS_PER_LINE {
@ -615,7 +616,6 @@ fn compact_lowerdir_option(opts: &[String]) -> (Option<PathBuf>, Vec<String>) {
} }
}; };
let idx = idx;
let common_dir = match get_longest_common_prefix(&lower_opts) { let common_dir = match get_longest_common_prefix(&lower_opts) {
None => return (None, n_opts), None => return (None, n_opts),
Some(v) => { Some(v) => {
@ -788,6 +788,21 @@ fn umount2<P: AsRef<Path>>(path: P, lazy_umount: bool) -> std::io::Result<()> {
nix::mount::umount2(path.as_ref(), flags).map_err(io::Error::from) nix::mount::umount2(path.as_ref(), flags).map_err(io::Error::from)
} }
pub fn get_mount_path(p: &Option<PathBuf>) -> String {
p.clone().unwrap_or_default().display().to_string()
}
pub fn get_mount_options(options: &Option<Vec<String>>) -> Vec<String> {
match options {
Some(o) => o.to_vec(),
None => vec![],
}
}
pub fn get_mount_type(typ: &Option<String>) -> String {
typ.clone().unwrap_or("bind".to_string())
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -84,7 +84,7 @@ pub fn get_node_map(cpus: &str) -> Result<HashMap<u32, Vec<u32>>> {
for c in cpuset.iter() { for c in cpuset.iter() {
let node_id = get_node_id(*c)?; let node_id = get_node_id(*c)?;
node_map.entry(node_id).or_insert_with(Vec::new).push(*c); node_map.entry(node_id).or_default().push(*c);
} }
Ok(node_map) Ok(node_map)

View File

@ -7,12 +7,12 @@
use anyhow::anyhow; use anyhow::anyhow;
#[cfg(any(target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64"))] #[cfg(any(target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64"))]
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use thiserror::Error; use thiserror::Error;
use serde::{Deserialize, Serialize};
#[cfg(any(target_arch = "s390x", target_arch = "powerpc64le"))] #[cfg(any(target_arch = "s390x", target_arch = "powerpc64le"))]
use nix::unistd::Uid; use nix::unistd::Uid;

View File

@ -4,9 +4,9 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
use std::path::PathBuf;
use kata_types::container::ContainerType; use kata_types::container::ContainerType;
use oci_spec::{runtime as oci, OciSpecError};
use std::path::PathBuf;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error { pub enum Error {
@ -18,7 +18,7 @@ pub enum Error {
MissingSandboxID, MissingSandboxID,
/// oci error /// oci error
#[error("oci error")] #[error("oci error")]
Oci(#[from] oci::Error), Oci(#[from] OciSpecError),
} }
const CRI_CONTAINER_TYPE_KEY_LIST: &[&str] = &[ const CRI_CONTAINER_TYPE_KEY_LIST: &[&str] = &[
@ -50,13 +50,15 @@ pub enum ShimIdInfo {
/// get container type /// get container type
pub fn get_container_type(spec: &oci::Spec) -> Result<ContainerType, Error> { pub fn get_container_type(spec: &oci::Spec) -> Result<ContainerType, Error> {
for k in CRI_CONTAINER_TYPE_KEY_LIST.iter() { if let Some(annotations) = spec.annotations() {
if let Some(type_value) = spec.annotations.get(*k) { for k in CRI_CONTAINER_TYPE_KEY_LIST.iter() {
match type_value.as_str() { if let Some(type_value) = annotations.get(*k) {
"sandbox" => return Ok(ContainerType::PodSandbox), match type_value.as_str() {
"podsandbox" => return Ok(ContainerType::PodSandbox), "sandbox" => return Ok(ContainerType::PodSandbox),
"container" => return Ok(ContainerType::PodContainer), "podsandbox" => return Ok(ContainerType::PodSandbox),
_ => return Err(Error::UnknownContainerType(type_value.clone())), "container" => return Ok(ContainerType::PodContainer),
_ => return Err(Error::UnknownContainerType(type_value.clone())),
}
} }
} }
} }
@ -70,11 +72,14 @@ pub fn get_shim_id_info() -> Result<ShimIdInfo, Error> {
match get_container_type(&spec)? { match get_container_type(&spec)? {
ContainerType::PodSandbox | ContainerType::SingleContainer => Ok(ShimIdInfo::Sandbox), ContainerType::PodSandbox | ContainerType::SingleContainer => Ok(ShimIdInfo::Sandbox),
ContainerType::PodContainer => { ContainerType::PodContainer => {
for k in CRI_SANDBOX_ID_KEY_LIST { if let Some(annotations) = spec.annotations() {
if let Some(sandbox_id) = spec.annotations.get(*k) { for k in CRI_SANDBOX_ID_KEY_LIST {
return Ok(ShimIdInfo::Container(sandbox_id.into())); if let Some(sandbox_id) = annotations.get(*k) {
return Ok(ShimIdInfo::Container(sandbox_id.into()));
}
} }
} }
Err(Error::MissingSandboxID) Err(Error::MissingSandboxID)
} }
} }
@ -86,9 +91,17 @@ pub fn get_bundle_path() -> std::io::Result<PathBuf> {
} }
/// load oci spec /// load oci spec
pub fn load_oci_spec() -> oci::Result<oci::Spec> { pub fn load_oci_spec() -> Result<oci::Spec, OciSpecError> {
let bundle_path = get_bundle_path()?; let bundle_path = get_bundle_path()?;
let spec_file = bundle_path.join("config.json"); let spec_file = bundle_path.join("config.json");
oci::Spec::load(spec_file.to_str().unwrap_or_default()) oci::Spec::load(spec_file.to_str().unwrap_or_default())
} }
/// handle string parsing for input possibly be JSON string.
pub fn parse_json_string(input: &str) -> &str {
let json_str: &str = serde_json::from_str(input).unwrap_or(input);
let stripped_str = json_str.strip_prefix("CAP_").unwrap_or(json_str);
stripped_str
}