libs/types: add kata-types crate under src/libs

Add kata-types crate to host constants and data types shared by multiple
Kata Containers components.

Fixes: #3305

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
Signed-off-by: Fupan Li <lifupan@gmail.com>
Signed-off-by: Huamin Tang <huamin.thm@alibaba-inc.com>
Signed-off-by: Lei Wang <wllenyj@linux.alibaba.com>
Signed-off-by: yanlei <yl.on.the.way@gmail.com>
This commit is contained in:
Liu Jiang 2021-12-09 11:51:38 +08:00 committed by Fupan Li
parent 4f62a7618c
commit 5b89c1df2f
13 changed files with 527 additions and 2 deletions

19
src/libs/Cargo.lock generated
View File

@ -283,6 +283,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "kata-types"
version = "0.1.0"
dependencies = [
"oci",
"serde",
"thiserror",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -414,6 +423,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "oci"
version = "0.1.0"
dependencies = [
"libc",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "once_cell"
version = "1.9.0"

View File

@ -1,7 +1,9 @@
[workspace]
members = [
"logging",
"kata-types",
"safe-path",
"protocols",
"oci",
]
resolver = "2"

View File

@ -5,6 +5,7 @@ or published to [`crates.io`](https://crates.io/index.html).
Currently it provides following library crates:
| Library | Description |
|-|-|-|
| [logging](logging/) | Facilities to setup logging subsystem based slog. |
|-|-|
| [logging](logging/) | Facilities to setup logging subsystem based on slog. |
| [types](kata-types/) | Collection of constants and data types shared by multiple Kata Containers components. |
| [safe-path](safe-path/) | Utilities to safely resolve filesystem paths. |

View File

@ -0,0 +1,19 @@
[package]
name = "kata-types"
version = "0.1.0"
description = "Constants and data types shared by Kata Containers components"
keywords = ["kata", "container", "runtime"]
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>"]
repository = "https://github.com/kata-containers/kata-containers.git"
homepage = "https://katacontainers.io/"
readme = "README.md"
license = "Apache-2.0"
edition = "2018"
[dependencies]
serde = { version = "1.0.100", features = ["derive"] }
thiserror = "1.0"
oci = { path = "../oci" }
[dev-dependencies]

View File

@ -0,0 +1,18 @@
# kata-types
This crate is a collection of constants and data types shared by multiple
[Kata Containers](https://github.com/kata-containers/kata-containers/) components.
It defines constants and data types used by multiple Kata Containers components. Those constants
and data types may be defined by Kata Containers or by other projects/specifications, such as:
- [Containerd](https://github.com/containerd/containerd)
- [Kubelet](https://github.com/kubernetes/kubelet)
## Support
**Operating Systems**:
- Linux
## License
This code is licensed under [Apache-2.0](../../../LICENSE).

View File

@ -0,0 +1,13 @@
// Copyright (c) 2019 Alibaba Cloud
// Copyright (c) 2019 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(missing_docs)]
pub const CONTAINER_TYPE_LABEL_KEY: &str = "io.kubernetes.cri.container-type";
pub const SANDBOX: &str = "sandbox";
pub const CONTAINER: &str = "container";
pub const SANDBOX_ID_LABEL_KEY: &str = "io.kubernetes.cri.sandbox-id";

View File

@ -0,0 +1,13 @@
// Copyright (c) 2019 Alibaba Cloud
// Copyright (c) 2019 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(missing_docs)]
pub const CONTAINER_TYPE_LABEL_KEY: &str = "io.kubernetes.cri.container-type";
pub const SANDBOX: &str = "sandbox";
pub const CONTAINER: &str = "container";
pub const SANDBOX_ID_LABEL_KEY: &str = "io.kubernetes.cri-o.SandboxID";

View File

@ -0,0 +1,13 @@
// Copyright (c) 2019 Alibaba Cloud
// Copyright (c) 2019 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(missing_docs)]
pub const CONTAINER_TYPE_LABEL_KEY: &str = "io.kubernetes.docker.type";
pub const SANDBOX: &str = "podsandbox";
pub const CONTAINER: &str = "container";
pub const SANDBOX_ID_LABEL_KEY: &str = "io.kubernetes.sandbox.id";

View File

@ -0,0 +1,14 @@
// Copyright (c) 2019 Alibaba Cloud
// Copyright (c) 2019 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
/// CRI-containerd specific annotations.
pub mod cri_containerd;
/// CRI-O specific annotations.
pub mod crio;
/// Dockershim specific annotations.
pub mod dockershim;

View File

@ -0,0 +1,203 @@
// Copyright (c) 2019-2021 Alibaba Cloud
// Copyright (c) 2019-2021 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
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";
const STATE_READY: &str = "ready";
const STATE_RUNNING: &str = "running";
const STATE_STOPPED: &str = "stopped";
const STATE_PAUSED: &str = "paused";
/// Error codes for container related operations.
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// Invalid container type
#[error("Invalid container type {0}")]
InvalidContainerType(String),
/// Invalid container state
#[error("Invalid sandbox state {0}")]
InvalidState(String),
/// Invalid container state transition
#[error("Can not transit from {0} to {1}")]
InvalidStateTransition(State, State),
}
/// Types of pod containers: container or sandbox.
#[derive(PartialEq, Debug, Clone)]
pub enum ContainerType {
/// A pod container.
PodContainer,
/// A pod sandbox.
PodSandbox,
}
impl ContainerType {
/// Check whether it's a pod container.
pub fn is_pod_container(&self) -> bool {
matches!(self, ContainerType::PodContainer)
}
/// Check whether it's a pod container.
pub fn is_pod_sandbox(&self) -> bool {
matches!(self, ContainerType::PodSandbox)
}
}
impl Display for ContainerType {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
ContainerType::PodContainer => write!(f, "{}", POD_CONTAINER),
ContainerType::PodSandbox => write!(f, "{}", POD_SANDBOX),
}
}
}
impl FromStr for ContainerType {
type Err = Error;
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),
_ => Err(Error::InvalidContainerType(value.to_owned())),
}
}
}
/// Process states.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum State {
/// The container is ready to run.
Ready,
/// The container executed the user-specified program but has not exited
Running,
/// The container has exited
Stopped,
/// The container has been paused.
Paused,
}
impl Display for State {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
State::Ready => write!(f, "{}", STATE_READY),
State::Running => write!(f, "{}", STATE_RUNNING),
State::Stopped => write!(f, "{}", STATE_STOPPED),
State::Paused => write!(f, "{}", STATE_PAUSED),
}
}
}
impl FromStr for State {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
STATE_READY => Ok(State::Ready),
STATE_RUNNING => Ok(State::Running),
STATE_STOPPED => Ok(State::Stopped),
STATE_PAUSED => Ok(State::Paused),
_ => Err(Error::InvalidState(value.to_owned())),
}
}
}
impl State {
/// Check whether it's a valid state transition from self to the `new_state`.
pub fn check_transition(self, new_state: State) -> Result<(), Error> {
match self {
State::Ready if new_state == State::Running || new_state == State::Stopped => Ok(()),
State::Running if new_state == State::Stopped => Ok(()),
State::Stopped if new_state == State::Running => Ok(()),
State::Paused if new_state == State::Paused => Ok(()),
_ => Err(Error::InvalidStateTransition(self, new_state)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_container_type() {
assert!(ContainerType::PodContainer.is_pod_container());
assert!(!ContainerType::PodContainer.is_pod_sandbox());
assert!(ContainerType::PodSandbox.is_pod_sandbox());
assert!(!ContainerType::PodSandbox.is_pod_container());
}
#[test]
fn test_container_type_display() {
assert_eq!(format!("{}", ContainerType::PodContainer), POD_CONTAINER);
assert_eq!(format!("{}", ContainerType::PodSandbox), POD_SANDBOX);
}
#[test]
fn test_container_type_from_str() {
assert_eq!(
ContainerType::from_str("pod_container").unwrap(),
ContainerType::PodContainer
);
assert_eq!(
ContainerType::from_str("container").unwrap(),
ContainerType::PodContainer
);
assert_eq!(
ContainerType::from_str("pod_sandbox").unwrap(),
ContainerType::PodSandbox
);
assert_eq!(
ContainerType::from_str("podsandbox").unwrap(),
ContainerType::PodSandbox
);
assert_eq!(
ContainerType::from_str("sandbox").unwrap(),
ContainerType::PodSandbox
);
ContainerType::from_str("test").unwrap_err();
}
#[test]
fn test_valid() {
let mut state = State::from_str("invalid_state");
assert!(state.is_err());
state = State::from_str("ready");
assert!(state.is_ok());
state = State::from_str("running");
assert!(state.is_ok());
state = State::from_str("stopped");
assert!(state.is_ok());
}
#[test]
fn test_valid_transition() {
use State::*;
assert!(Ready.check_transition(Ready).is_err());
assert!(Ready.check_transition(Running).is_ok());
assert!(Ready.check_transition(Stopped).is_ok());
assert!(Running.check_transition(Ready).is_err());
assert!(Running.check_transition(Running).is_err());
assert!(Running.check_transition(Stopped).is_ok());
assert!(Stopped.check_transition(Ready).is_err());
assert!(Stopped.check_transition(Running).is_ok());
assert!(Stopped.check_transition(Stopped).is_err());
}
}

View File

@ -0,0 +1,102 @@
// Copyright (c) 2019-2021 Alibaba Cloud
// Copyright (c) 2019-2021 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
use std::path::Path;
use crate::annotations;
use crate::container::ContainerType;
use std::str::FromStr;
// K8S_EMPTY_DIR is the k8s specific path for `empty-dir` volumes
const K8S_EMPTY_DIR: &str = "kubernetes.io~empty-dir";
/// Check whether the path is a K8S empty directory.
///
/// For a K8S EmptyDir, Kubernetes mounts
/// "/var/lib/kubelet/pods/<id>/volumes/kubernetes.io~empty-dir/<volumeMount name>"
/// to "/<mount-point>".
pub fn is_empty_dir<P: AsRef<Path>>(path: P) -> bool {
let path = path.as_ref();
if let Some(parent) = path.parent() {
if let Some(pname) = parent.file_name() {
if pname == K8S_EMPTY_DIR && parent.parent().is_some() {
return true;
}
}
}
false
}
/// Get K8S container type from OCI annotations.
pub fn container_type(spec: &oci::Spec) -> ContainerType {
// PodSandbox: "sandbox" (Containerd & CRI-O), "podsandbox" (dockershim)
// PodContainer: "container" (Containerd & CRI-O & dockershim)
for k in [
annotations::crio::CONTAINER_TYPE_LABEL_KEY,
annotations::cri_containerd::CONTAINER_TYPE_LABEL_KEY,
annotations::dockershim::CONTAINER_TYPE_LABEL_KEY,
]
.iter()
{
if let Some(v) = spec.annotations.get(k.to_owned()) {
if let Ok(t) = ContainerType::from_str(v) {
return t;
}
}
}
ContainerType::PodSandbox
}
/// 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()));
}
}
Ok(None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_empty_dir() {
let empty_dir = "/volumes/kubernetes.io~empty-dir/shm";
assert!(is_empty_dir(empty_dir));
let empty_dir = "/volumes/kubernetes.io~empty-dir//shm";
assert!(is_empty_dir(empty_dir));
let empty_dir = "/volumes/kubernetes.io~empty-dir-test/shm";
assert!(!is_empty_dir(empty_dir));
let empty_dir = "/volumes/kubernetes.io~empty-dir";
assert!(!is_empty_dir(empty_dir));
let empty_dir = "kubernetes.io~empty-dir";
assert!(!is_empty_dir(empty_dir));
let empty_dir = "/kubernetes.io~empty-dir/shm";
assert!(is_empty_dir(empty_dir));
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2021 Alibaba Cloud
//
// SPDX-License-Identifier: Apache-2.0
//
//! Constants and Data Types shared by Kata Containers components.
#[deny(missing_docs)]
/// Constants and data types annotations.
pub mod annotations;
/// Constants and data types related to container.
pub mod container;
/// Constants and data types related to Kubernetes/kubelet.
pub mod k8s;
/// Constants and data types related to mount point.
pub mod mount;

View File

@ -0,0 +1,88 @@
// Copyright (c) 2019-2021 Alibaba Cloud
// Copyright (c) 2019-2021 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
use std::path::PathBuf;
/// Prefix to mark a volume as Kata special.
pub const KATA_VOLUME_TYPE_PREFIX: &str = "kata:";
/// The Mount should be ignored by the host and handled by the guest.
pub const KATA_GUEST_MOUNT_PREFIX: &str = "kata:guest-mount:";
/// KATA_EPHEMERAL_DEV_TYPE creates a tmpfs backed volume for sharing files between containers.
pub const KATA_EPHEMERAL_VOLUME_TYPE: &str = "kata:ephemeral";
/// KATA_HOST_DIR_TYPE use for host empty dir
pub const KATA_HOST_DIR_VOLUME_TYPE: &str = "kata:hostdir";
/// Information about a mount.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct Mount {
/// A device name, but can also be a file or directory name for bind mounts or a dummy.
/// Path values for bind mounts are either absolute or relative to the bundle. A mount is a
/// bind mount if it has either bind or rbind in the options.
pub source: String,
/// Destination of mount point: path inside container. This value MUST be an absolute path.
pub destination: PathBuf,
/// The type of filesystem for the mountpoint.
pub fs_type: String,
/// Mount options for the mountpoint.
pub options: Vec<String>,
/// Optional device id for the block device when:
/// - the source is a block device or a mountpoint for a block device
/// - block device direct assignment is enabled
pub device_id: Option<String>,
/// Intermediate path to mount the source on host side and then passthrough to vm by shared fs.
pub host_shared_fs_path: Option<PathBuf>,
/// Whether to mount the mountpoint in readonly mode
pub read_only: bool,
}
impl Mount {
/// Get size of mount options.
pub fn option_size(&self) -> usize {
self.options.iter().map(|v| v.len() + 1).sum()
}
}
/// Check whether a mount type is a marker for Kata specific volume.
pub fn is_kata_special_volume(ty: &str) -> bool {
ty.len() > KATA_VOLUME_TYPE_PREFIX.len() && ty.starts_with(KATA_VOLUME_TYPE_PREFIX)
}
/// Check whether a mount type is a marker for Kata guest mount volume.
pub fn is_kata_guest_mount_volume(ty: &str) -> bool {
ty.len() > KATA_GUEST_MOUNT_PREFIX.len() && ty.starts_with(KATA_GUEST_MOUNT_PREFIX)
}
/// Check whether a mount type is a marker for Kata ephemeral volume.
pub fn is_kata_ephemeral_volume(ty: &str) -> bool {
ty == KATA_EPHEMERAL_VOLUME_TYPE
}
/// Check whether a mount type is a marker for Kata hostdir volume.
pub fn is_kata_host_dir_volume(ty: &str) -> bool {
ty == KATA_HOST_DIR_VOLUME_TYPE
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_kata_special_volume() {
assert!(is_kata_special_volume("kata:guest-mount:nfs"));
assert!(!is_kata_special_volume("kata:"));
}
#[test]
fn test_is_kata_guest_mount_volume() {
assert!(is_kata_guest_mount_volume("kata:guest-mount:nfs"));
assert!(!is_kata_guest_mount_volume("kata:guest-mount"));
assert!(!is_kata_guest_mount_volume("kata:guest-moun"));
assert!(!is_kata_guest_mount_volume("Kata:guest-mount:nfs"));
}
}