iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0
Cloud Native

關於 WebAssembly 也能變成 Container 的這檔事系列 第 10

Wasm+containerd-shim-wasm+sandbox - part 1

  • 分享至 

  • xImage
  •  

Wasm+containerd-shim-wasm+sandbox - part 1

今天讓我們進入 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"),
        }
    }
}

上一篇
Wasm+containerd-shim-wasm+container
下一篇
Wasm+containerd-shim-wasm+sandbox - part 2
系列文
關於 WebAssembly 也能變成 Container 的這檔事15
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言