今天讓我們進入 sandbox 這個資料夾吧,由於裡面檔案比較多,我們會分多天來看:
sandbox
資料夾結構sandbox
├── cli.rs
├── error.rs (*)
├── instance.rs
├── instance_utils.rs
├── manager.rs
├── mod.rs (*)
├── oci.rs
├── shim.rs
└── stdio.rs
sandbox/mod.rs
庖丁解牛,總是要先知道 sandbox 模組的主要組成,宏觀地知道存在哪些模組。
//! Abstracts the sandboxing environment and execution context for a container.
//! sandbox 模組抽象化了容器(container)的沙盒環境(sandbox environment)和執行的上下文(execution context)。
// 引入 crate 內的 services 中的 sandbox,後續會在 services 中看到內容。
use crate::services::sandbox;
pub mod cli; // 負責命令列介面的模組
pub mod error; // 負責錯誤處理的模組
pub mod instance; // 負責實例(Instance)相關功能的模組
pub mod instance_utils; // 提供實例(Instance)的實用函式
pub mod manager; // 負責管理沙盒(Sandbox)的模組
pub mod shim; // 負責沙盒(Sandbox)的 Shim
pub mod stdio; // 負責標準輸入/輸出(Stdio)的模組
// 將子模組中的重要資料結構公開導出
pub use error::{Error, Result};
pub use instance::{Instance, InstanceConfig};
pub use manager::{Sandbox as SandboxService, Service as ManagerService};
pub use shim::{Cli as ShimCli, Local};
pub use stdio::Stdio;
// 定義僅限 crate 內部使用的 OCI 模組
pub(crate) mod oci; // 負責與 Open Container Initiative(OCI)相關的功能
sandbox/error.rs
有道是「遇事不決,先看錯誤」,所以我們先來看看錯誤處理的模組吧。
//! Error types used by shims
//! This handles converting to the appropriate ttrpc error codes
//! 在 error 模組中,定義了由 shims 所使用的錯誤型態
//! 此模組同時也負責將這些錯誤轉換為對應的 ttrpc 錯誤碼
use anyhow::Error as AnyError;
use containerd_shim::Error as ShimError;
use oci_spec::OciSpecError;
use thiserror::Error;
use ttrpc;
// 定義 Error 列舉型態,你想到的多種可能錯誤都在裡頭,如果遇到不在裡面的錯誤,請發 PR 修它XD
#[derive(Debug, Error)]
pub enum Error {
/// An error occurred while parsing the OCI spec
/// 解析 OCI 時發生的錯誤
#[error("{0}")]
Oci(#[from] OciSpecError),
/// An error that can occur while setting up the environment for the container
/// 設定容器環境時發生的錯誤
#[error("{0}")]
Stdio(#[from] std::io::Error),
#[error("{0}")]
Others(String),
/// Errors to/from the containerd shim library.
/// 來自 containerd shim 函式庫的錯誤
#[error("{0}")]
Shim(#[from] ShimError),
/// Requested item is not found
/// 請求的物件不存在
#[error("not found: {0}")]
NotFound(String),
/// Requested item already exists
/// 請求的物件已存在
#[error("already exists: {0}")]
AlreadyExists(String),
/// Supplied arguments/options/config is invalid
/// 提供的參數/選項/設定無效
#[error("invalid argument: {0}")]
InvalidArgument(String),
/// Any other error
/// 其他錯誤
#[error("{0}")]
Any(#[from] AnyError),
/// The operation was rejected because the system is not in a state required for the operation's
/// 因系統不符合操作所需的狀態,因此該操作被拒絕
#[error("{0}")]
FailedPrecondition(String),
/// Error while parsing JSON
/// 解析 JSON 時發生的錯誤
#[error("{0}")]
Json(#[from] serde_json::Error),
/// Error from the system
/// 系統錯誤
#[cfg(unix)]
#[error("{0}")]
Errno(#[from] nix::errno::Errno),
/// Errors from libcontainer
/// 來自 libcontainer 的錯誤
#[cfg(unix)]
#[error("{0}")]
Libcontainer(#[from] libcontainer::error::LibcontainerError),
}
// 定義 Result 型態,簡化語法
pub type Result<T> = ::std::result::Result<T, Error>;
// 實作從 Error 到 ttrpc::Error 的映射
impl From<Error> for ttrpc::Error {
fn from(e: Error) -> Self {
match e {
Error::Shim(ref s) => match s {
ShimError::InvalidArgument(s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::INVALID_ARGUMENT, s))
}
ShimError::NotFoundError(s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::NOT_FOUND, s))
}
_ => ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::UNKNOWN, s)),
},
Error::NotFound(ref s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::NOT_FOUND, s))
}
Error::AlreadyExists(ref s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::ALREADY_EXISTS, s))
}
Error::InvalidArgument(ref s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::INVALID_ARGUMENT, s))
}
Error::FailedPrecondition(ref s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::FAILED_PRECONDITION, s))
}
Error::Oci(ref _s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::UNKNOWN, e.to_string()))
}
Error::Any(ref s) => {
ttrpc::Error::RpcStatus(ttrpc::get_status(ttrpc::Code::UNKNOWN, s))
}
_ => ttrpc::Error::Others(e.to_string()),
}
}
}
// 定義單元測試,看測試可以理解上面的實作細節
#[cfg(test)]
mod tests {
use thiserror::Error;
use super::*;
#[derive(Debug, Error)]
enum TestError {
#[error("{0}")]
AnError(String),
}
#[test]
fn test_error_to_ttrpc_status() {
let e = Error::InvalidArgument("invalid argument".to_string());
let t: ttrpc::Error = e.into();
match t {
ttrpc::Error::RpcStatus(s) => {
assert_eq!(s.code(), ttrpc::Code::INVALID_ARGUMENT);
assert_eq!(s.message, "invalid argument");
}
_ => panic!("unexpected error"),
}
let e = Error::NotFound("not found".to_string());
let t: ttrpc::Error = e.into();
match t {
ttrpc::Error::RpcStatus(s) => {
assert_eq!(s.code(), ttrpc::Code::NOT_FOUND);
assert_eq!(s.message, "not found");
}
_ => panic!("unexpected error"),
}
let e = Error::AlreadyExists("already exists".to_string());
let t: ttrpc::Error = e.into();
match t {
ttrpc::Error::RpcStatus(s) => {
assert_eq!(s.code(), ttrpc::Code::ALREADY_EXISTS);
assert_eq!(s.message, "already exists");
}
_ => panic!("unexpected error"),
}
let e = Error::FailedPrecondition("failed precondition".to_string());
let t: ttrpc::Error = e.into();
match t {
ttrpc::Error::RpcStatus(s) => {
assert_eq!(s.code(), ttrpc::Code::FAILED_PRECONDITION);
assert_eq!(s.message, "failed precondition");
}
_ => panic!("unexpected error"),
}
let e = Error::Shim(ShimError::InvalidArgument("invalid argument".to_string()));
let t: ttrpc::Error = e.into();
match t {
ttrpc::Error::RpcStatus(s) => {
assert_eq!(s.code(), ttrpc::Code::INVALID_ARGUMENT);
assert_eq!(s.message, "invalid argument");
}
_ => panic!("unexpected error"),
}
let e = Error::Any(AnyError::new(TestError::AnError("any error".to_string())));
let t: ttrpc::Error = e.into();
match t {
ttrpc::Error::RpcStatus(s) => {
assert_eq!(s.code(), ttrpc::Code::UNKNOWN);
assert_eq!(s.message, "any error");
}
_ => panic!("unexpected error"),
}
}
}