libs: enhance kata-sys-util

1. move verify_cid from agent to libs/kata-sys-util
2. enhance kata-sys-util/k8s

Signed-off-by: Quanwei Zhou <quanweiZhou@linux.alibaba.com>
This commit is contained in:
Quanwei Zhou 2022-03-03 14:24:08 +08:00 committed by Fupan Li
parent 69ba1ae9e4
commit 641b736106
10 changed files with 495 additions and 277 deletions

76
src/agent/Cargo.lock generated
View File

@ -214,6 +214,12 @@ dependencies = [
"syn",
]
[[package]]
name = "common-path"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@ -311,6 +317,17 @@ dependencies = [
"libc",
]
[[package]]
name = "fail"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3245a0ca564e7f3c797d20d833a6870f57a728ac967d5225b3ffdef4465011"
dependencies = [
"lazy_static",
"log",
"rand",
]
[[package]]
name = "fixedbitset"
version = "0.2.0"
@ -440,6 +457,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -571,6 +594,7 @@ dependencies = [
"clap",
"futures",
"ipnetwork",
"kata-sys-util",
"lazy_static",
"libc",
"log",
@ -608,6 +632,44 @@ dependencies = [
"vsock-exporter",
]
[[package]]
name = "kata-sys-util"
version = "0.1.0"
dependencies = [
"cgroups-rs",
"chrono",
"common-path",
"fail",
"kata-types",
"lazy_static",
"libc",
"nix 0.23.1",
"oci",
"once_cell",
"serde_json",
"slog",
"slog-scope",
"subprocess",
"thiserror",
]
[[package]]
name = "kata-types"
version = "0.1.0"
dependencies = [
"glob",
"lazy_static",
"num_cpus",
"oci",
"regex",
"serde",
"serde_json",
"slog",
"slog-scope",
"thiserror",
"toml",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -863,9 +925,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
@ -1552,6 +1614,16 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subprocess"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "055cf3ebc2981ad8f0a5a17ef6652f652d87831f79fddcba2ac57bcb9a0aa407"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.82"

View File

@ -20,6 +20,7 @@ scopeguard = "1.0.0"
thiserror = "1.0.26"
regex = "1.5.4"
serial_test = "0.5.1"
kata-sys-util = { path = "../libs/kata-sys-util" }
sysinfo = "0.23.0"
# Async helpers

View File

@ -134,30 +134,6 @@ pub struct AgentService {
sandbox: Arc<Mutex<Sandbox>>,
}
// A container ID must match this regex:
//
// ^[a-zA-Z0-9][a-zA-Z0-9_.-]+$
//
fn verify_cid(id: &str) -> Result<()> {
let mut chars = id.chars();
let valid = match chars.next() {
Some(first)
if first.is_alphanumeric()
&& id.len() > 1
&& chars.all(|c| c.is_alphanumeric() || ['.', '-', '_'].contains(&c)) =>
{
true
}
_ => false,
};
match valid {
true => Ok(()),
false => Err(anyhow!("invalid container ID: {:?}", id)),
}
}
impl AgentService {
#[instrument]
async fn do_create_container(
@ -166,7 +142,7 @@ impl AgentService {
) -> Result<()> {
let cid = req.container_id.clone();
verify_cid(&cid)?;
kata_sys_util::validate::verify_cid(&cid)?;
let mut oci_spec = req.OCI.clone();
let use_sandbox_pidns = req.get_sandbox_pidns();
@ -2674,233 +2650,6 @@ OtherField:other
}
}
#[tokio::test]
async fn test_verify_cid() {
#[derive(Debug)]
struct TestData<'a> {
id: &'a str,
expect_error: bool,
}
let tests = &[
TestData {
// Cannot be blank
id: "",
expect_error: true,
},
TestData {
// Cannot be a space
id: " ",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: ".",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "-",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "_",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: " a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: ".a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "-a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "_a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "..",
expect_error: true,
},
TestData {
// Too short
id: "a",
expect_error: true,
},
TestData {
// Too short
id: "z",
expect_error: true,
},
TestData {
// Too short
id: "A",
expect_error: true,
},
TestData {
// Too short
id: "Z",
expect_error: true,
},
TestData {
// Too short
id: "0",
expect_error: true,
},
TestData {
// Too short
id: "9",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "-1",
expect_error: true,
},
TestData {
id: "/",
expect_error: true,
},
TestData {
id: "a/",
expect_error: true,
},
TestData {
id: "a/../",
expect_error: true,
},
TestData {
id: "../a",
expect_error: true,
},
TestData {
id: "../../a",
expect_error: true,
},
TestData {
id: "../../../a",
expect_error: true,
},
TestData {
id: "foo/../bar",
expect_error: true,
},
TestData {
id: "foo bar",
expect_error: true,
},
TestData {
id: "a.",
expect_error: false,
},
TestData {
id: "a..",
expect_error: false,
},
TestData {
id: "aa",
expect_error: false,
},
TestData {
id: "aa.",
expect_error: false,
},
TestData {
id: "hello..world",
expect_error: false,
},
TestData {
id: "hello/../world",
expect_error: true,
},
TestData {
id: "aa1245124sadfasdfgasdga.",
expect_error: false,
},
TestData {
id: "aAzZ0123456789_.-",
expect_error: false,
},
TestData {
id: "abcdefghijklmnopqrstuvwxyz0123456789.-_",
expect_error: false,
},
TestData {
id: "0123456789abcdefghijklmnopqrstuvwxyz.-_",
expect_error: false,
},
TestData {
id: " abcdefghijklmnopqrstuvwxyz0123456789.-_",
expect_error: true,
},
TestData {
id: ".abcdefghijklmnopqrstuvwxyz0123456789.-_",
expect_error: true,
},
TestData {
id: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_",
expect_error: false,
},
TestData {
id: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.-_",
expect_error: false,
},
TestData {
id: " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_",
expect_error: true,
},
TestData {
id: ".ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_",
expect_error: true,
},
TestData {
id: "/a/b/c",
expect_error: true,
},
TestData {
id: "a/b/c",
expect_error: true,
},
TestData {
id: "foo/../../../etc/passwd",
expect_error: true,
},
TestData {
id: "../../../../../../etc/motd",
expect_error: true,
},
TestData {
id: "/etc/passwd",
expect_error: true,
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = verify_cid(d.id);
let msg = format!("{}, result: {:?}", msg, result);
if result.is_ok() {
assert!(!d.expect_error, "{}", msg);
} else {
assert!(d.expect_error, "{}", msg);
}
}
}
#[tokio::test]
async fn test_volume_capacity_stats() {
skip_if_not_root!();

View File

@ -13,8 +13,16 @@ use std::process::Command;
use crate::{eother, sl};
// nix filesystem_type for different target_os
#[cfg(all(target_os = "linux", target_env = "musl"))]
type FsType = libc::c_ulong;
#[cfg(all(target_os = "linux", not(any(target_env = "musl"))))]
type FsType = libc::__fsword_t;
// from linux.git/fs/fuse/inode.c: #define FUSE_SUPER_MAGIC 0x65735546
const FUSE_SUPER_MAGIC: u32 = 0x65735546;
const FUSE_SUPER_MAGIC: FsType = 0x65735546;
// from linux.git/include/uapi/linux/magic.h
const OVERLAYFS_SUPER_MAGIC: FsType = 0x794c7630;
/// Get bundle path (current working directory).
pub fn get_bundle_path() -> Result<PathBuf> {
@ -35,7 +43,7 @@ pub fn get_base_name<P: AsRef<Path>>(src: P) -> Result<OsString> {
/// Check whether `path` is on a fuse filesystem.
pub fn is_fuse_fs<P: AsRef<Path>>(path: P) -> bool {
if let Ok(st) = nix::sys::statfs::statfs(path.as_ref()) {
if st.filesystem_type().0 == FUSE_SUPER_MAGIC as i64 {
if st.filesystem_type().0 == FUSE_SUPER_MAGIC {
return true;
}
}
@ -45,7 +53,7 @@ pub fn is_fuse_fs<P: AsRef<Path>>(path: P) -> bool {
/// Check whether `path` is on a overlay filesystem.
pub fn is_overlay_fs<P: AsRef<Path>>(path: P) -> bool {
if let Ok(st) = nix::sys::statfs::statfs(path.as_ref()) {
if st.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
if st.filesystem_type().0 == OVERLAYFS_SUPER_MAGIC {
return true;
}
}

View File

@ -13,6 +13,7 @@ pub mod hooks;
pub mod k8s;
pub mod mount;
pub mod numa;
pub mod validate;
// Convenience macro to obtain the scoped logger
#[macro_export]

View File

@ -0,0 +1,267 @@
// Copyright (c) 2019-2022 Alibaba Cloud
// Copyright (c) 2019-2022 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("invalid container ID {0}")]
InvalidContainerID(String),
}
// A container ID must match this regex:
//
// ^[a-zA-Z0-9][a-zA-Z0-9_.-]+$
//
pub fn verify_cid(id: &str) -> Result<(), Error> {
let mut chars = id.chars();
let valid = match chars.next() {
Some(first)
if first.is_alphanumeric()
&& id.len() > 1
&& chars.all(|c| c.is_alphanumeric() || ['.', '-', '_'].contains(&c)) =>
{
true
}
_ => false,
};
match valid {
true => Ok(()),
false => Err(Error::InvalidContainerID(id.to_string())),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_verify_cid() {
#[derive(Debug)]
struct TestData<'a> {
id: &'a str,
expect_error: bool,
}
let tests = &[
TestData {
// Cannot be blank
id: "",
expect_error: true,
},
TestData {
// Cannot be a space
id: " ",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: ".",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "-",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "_",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: " a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: ".a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "-a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "_a",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "..",
expect_error: true,
},
TestData {
// Too short
id: "a",
expect_error: true,
},
TestData {
// Too short
id: "z",
expect_error: true,
},
TestData {
// Too short
id: "A",
expect_error: true,
},
TestData {
// Too short
id: "Z",
expect_error: true,
},
TestData {
// Too short
id: "0",
expect_error: true,
},
TestData {
// Too short
id: "9",
expect_error: true,
},
TestData {
// Must start with an alphanumeric
id: "-1",
expect_error: true,
},
TestData {
id: "/",
expect_error: true,
},
TestData {
id: "a/",
expect_error: true,
},
TestData {
id: "a/../",
expect_error: true,
},
TestData {
id: "../a",
expect_error: true,
},
TestData {
id: "../../a",
expect_error: true,
},
TestData {
id: "../../../a",
expect_error: true,
},
TestData {
id: "foo/../bar",
expect_error: true,
},
TestData {
id: "foo bar",
expect_error: true,
},
TestData {
id: "a.",
expect_error: false,
},
TestData {
id: "a..",
expect_error: false,
},
TestData {
id: "aa",
expect_error: false,
},
TestData {
id: "aa.",
expect_error: false,
},
TestData {
id: "hello..world",
expect_error: false,
},
TestData {
id: "hello/../world",
expect_error: true,
},
TestData {
id: "aa1245124sadfasdfgasdga.",
expect_error: false,
},
TestData {
id: "aAzZ0123456789_.-",
expect_error: false,
},
TestData {
id: "abcdefghijklmnopqrstuvwxyz0123456789.-_",
expect_error: false,
},
TestData {
id: "0123456789abcdefghijklmnopqrstuvwxyz.-_",
expect_error: false,
},
TestData {
id: " abcdefghijklmnopqrstuvwxyz0123456789.-_",
expect_error: true,
},
TestData {
id: ".abcdefghijklmnopqrstuvwxyz0123456789.-_",
expect_error: true,
},
TestData {
id: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_",
expect_error: false,
},
TestData {
id: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.-_",
expect_error: false,
},
TestData {
id: " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_",
expect_error: true,
},
TestData {
id: ".ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_",
expect_error: true,
},
TestData {
id: "/a/b/c",
expect_error: true,
},
TestData {
id: "a/b/c",
expect_error: true,
},
TestData {
id: "foo/../../../etc/passwd",
expect_error: true,
},
TestData {
id: "../../../../../../etc/motd",
expect_error: true,
},
TestData {
id: "/etc/passwd",
expect_error: true,
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = verify_cid(d.id);
let msg = format!("{}, result: {:?}", msg, result);
if result.is_ok() {
assert!(!d.expect_error, "{}", msg);
} else {
assert!(d.expect_error, "{}", msg);
}
}
}
}

1
src/libs/kata-types/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
Cargo.lock

View File

@ -7,11 +7,17 @@
use std::fmt::{Display, Formatter};
use std::str::FromStr;
const CONTAINER: &str = "container";
const SANDBOX: &str = "sandbox";
const POD_CONTAINER: &str = "pod_container";
const POD_SANDBOX: &str = "pod_sandbox";
const POD_SANDBOX2: &str = "podsandbox";
// a container running within a pod
pub(crate) const POD_CONTAINER: &str = "pod_container";
// cri containerd/crio/docker: a container running within a pod
pub(crate) const CONTAINER: &str = "container";
// a pod sandbox container
pub(crate) const POD_SANDBOX: &str = "pod_sandbox";
// cri containerd/crio: a pod sandbox container
pub(crate) const SANDBOX: &str = "sandbox";
// docker: a sandbox sandbox container
pub(crate) const PODSANDBOX: &str = "podsandbox";
const STATE_READY: &str = "ready";
const STATE_RUNNING: &str = "running";
@ -68,7 +74,7 @@ impl FromStr for ContainerType {
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
POD_CONTAINER | CONTAINER => Ok(ContainerType::PodContainer),
POD_SANDBOX | POD_SANDBOX2 | SANDBOX => Ok(ContainerType::PodSandbox),
POD_SANDBOX | PODSANDBOX | SANDBOX => Ok(ContainerType::PodSandbox),
_ => Err(Error::InvalidContainerType(value.to_owned())),
}
}

View File

@ -56,28 +56,31 @@ pub fn container_type(spec: &oci::Spec) -> ContainerType {
/// Determine the k8s sandbox ID from OCI annotations.
///
/// This function is expected to be called only when the container type is "PodContainer".
pub fn sandbox_id(spec: &oci::Spec) -> Result<Option<String>, String> {
if container_type(spec) != ContainerType::PodSandbox {
return Err("Not a sandbox container".to_string());
}
for k in [
annotations::crio::SANDBOX_ID_LABEL_KEY,
annotations::cri_containerd::SANDBOX_ID_LABEL_KEY,
annotations::dockershim::SANDBOX_ID_LABEL_KEY,
]
.iter()
{
if let Some(id) = spec.annotations.get(k.to_owned()) {
return Ok(Some(id.to_string()));
pub fn container_type_with_id(spec: &oci::Spec) -> (ContainerType, Option<String>) {
let container_type = container_type(spec);
let mut sid = None;
if container_type == ContainerType::PodContainer {
for k in [
annotations::crio::SANDBOX_ID_LABEL_KEY,
annotations::cri_containerd::SANDBOX_ID_LABEL_KEY,
annotations::dockershim::SANDBOX_ID_LABEL_KEY,
]
.iter()
{
if let Some(id) = spec.annotations.get(k.to_owned()) {
sid = Some(id.to_string());
break;
}
}
}
Ok(None)
(container_type, sid)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{annotations, container};
#[test]
fn test_is_empty_dir() {
@ -99,4 +102,112 @@ mod tests {
let empty_dir = "/kubernetes.io~empty-dir/shm";
assert!(is_empty_dir(empty_dir));
}
#[test]
fn test_container_type() {
let sid = "sid".to_string();
let mut spec = oci::Spec::default();
// default
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodSandbox, None)
);
// crio sandbox
spec.annotations = [(
annotations::crio::CONTAINER_TYPE_LABEL_KEY.to_string(),
container::SANDBOX.to_string(),
)]
.iter()
.cloned()
.collect();
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodSandbox, None)
);
// cri containerd sandbox
spec.annotations = [(
annotations::crio::CONTAINER_TYPE_LABEL_KEY.to_string(),
container::POD_SANDBOX.to_string(),
)]
.iter()
.cloned()
.collect();
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodSandbox, None)
);
// docker shim sandbox
spec.annotations = [(
annotations::crio::CONTAINER_TYPE_LABEL_KEY.to_string(),
container::PODSANDBOX.to_string(),
)]
.iter()
.cloned()
.collect();
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodSandbox, None)
);
// crio container
spec.annotations = [
(
annotations::crio::CONTAINER_TYPE_LABEL_KEY.to_string(),
container::CONTAINER.to_string(),
),
(
annotations::crio::SANDBOX_ID_LABEL_KEY.to_string(),
sid.clone(),
),
]
.iter()
.cloned()
.collect();
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodContainer, Some(sid.clone()))
);
// cri containerd container
spec.annotations = [
(
annotations::cri_containerd::CONTAINER_TYPE_LABEL_KEY.to_string(),
container::POD_CONTAINER.to_string(),
),
(
annotations::cri_containerd::SANDBOX_ID_LABEL_KEY.to_string(),
sid.clone(),
),
]
.iter()
.cloned()
.collect();
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodContainer, Some(sid.clone()))
);
// docker shim container
spec.annotations = [
(
annotations::dockershim::CONTAINER_TYPE_LABEL_KEY.to_string(),
container::CONTAINER.to_string(),
),
(
annotations::dockershim::SANDBOX_ID_LABEL_KEY.to_string(),
sid.clone(),
),
]
.iter()
.cloned()
.collect();
assert_eq!(
container_type_with_id(&spec),
(ContainerType::PodContainer, Some(sid))
);
}
}

View File

@ -1,9 +1,11 @@
Cargo.lock
src/agent.rs
src/agent_ttrpc.rs
src/agent_ttrpc_async.rs
src/csi.rs
src/empty.rs
src/health.rs
src/health_ttrpc.rs
src/health_ttrpc_async.rs
src/oci.rs
src/types.rs