iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0
Cloud Native

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

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

  • 分享至 

  • xImage
  •  

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

sandbox 資料夾結構

sandbox
├── cli.rs (part 2)
├── error.rs (part 1)
├── instance.rs
├── instance_utils.rs
├── manager.rs (part 3)
├── mod.rs (part 1)
├── oci.rs (*)
├── shim.rs
└── stdio.rs

oci.rs

由於每個 runtime 的實作都會需要利用到 OCI 規範中的資訊,因此 oci.rs 將這些資訊抽象化成一個模組,讓每個 runtime 都可以共用,而不是各自實作自己的 OCI 處理函式。

//! Generic helpers for working with OCI specs that can be consumed by any runtime.
//! 通用的 OCI 規格輔助工具,確保 OCI 規格可以被任何 runtime 所消耗。
use std::collections::HashMap;
use std::io::{ErrorKind, Write};
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::process;

use anyhow::Context;
pub use oci_spec::runtime::Spec;

use super::error::Result;

// 處理環境變數
fn parse_env(envs: &[String]) -> HashMap<String, String> {
    // make NAME=VALUE to HashMap<NAME, VALUE>.
    // 將 NAME=VALUE 轉換成 HashMap<NAME, VALUE>
    // 例如: ["PATH=/usr/bin", "HOME=/home"] => {"PATH": "/usr/bin", "HOME": "/home"}
    envs.iter()
        .filter_map(|e| {
            let mut split = e.split('='); // 環境變數的分隔符號必定為 "="

            split.next().map(|key| {
                let value = split.collect::<Vec<&str>>().join("=");
                (key.into(), value)
            })
        })
        .collect()
}

// 設定 prestart hooks
pub(crate) fn setup_prestart_hooks(hooks: &Option<oci_spec::runtime::Hooks>) -> Result<()> {
    if let Some(hooks) = hooks {
        let prestart_hooks = hooks.prestart().as_ref().unwrap();

        for hook in prestart_hooks {
            let mut hook_command = process::Command::new(hook.path());
            // Based on OCI spec, the first argument of the args vector is the
            // arg0, which can be different from the path.  For example, path
            // may be "/usr/bin/true" and arg0 is set to "true". However, rust
            // command differentiates arg0 from args, where rust command arg
            // doesn't include arg0. So we have to make the split arg0 from the
            // rest of args.
            // 根據 OCI 規範,第一個參數 arg0 可以與路徑相異。
            // 例如: 路徑可能是 "/usr/bin/true",而 arg0 可能是 "true"。
            // 然而,rust command 會將 arg0 與 args 區分開來,在 rust command 的 arg 將不包含 arg0。
            // 因此我們必須在此將 arg0 與 args 區分開來,以符合規範。
            if let Some((arg0, args)) = hook.args().as_ref().and_then(|a| a.split_first()) {
                log::debug!("run_hooks arg0: {:?}, args: {:?}", arg0, args);

                // 在 Unix-like 系統中,可以使用 Command::arg0() 來設定 arg0
                #[cfg(unix)]
                {
                    hook_command.arg0(arg0).args(args);
                }

                #[cfg(windows)]
                {
                    // 檢查 arg0 是否與執行檔名相同,若不同則回傳錯誤
                    if !&hook.path().ends_with(arg0) {
                        return Err(crate::sandbox::Error::InvalidArgument("Running with arg0 as different name than executable is not supported on Windows due to rust std library process implementation.".to_string()));
                    }

                    hook_command.args(args);
                }
            } else {
                #[cfg(unix)]
                hook_command.arg0(hook.path());
            };

            // 處理環境變數
            let envs: HashMap<String, String> = if let Some(env) = hook.env() {
                parse_env(env)
            } else {
                HashMap::new()
            };
            log::debug!("run_hooks envs: {:?}", envs);

            // 執行 prestart hooks
            let mut hook_process = hook_command
                .env_clear()
                .envs(envs)
                .stdin(process::Stdio::piped())
                .spawn()
                .with_context(|| "Failed to execute hook")?;

            if let Some(stdin) = &mut hook_process.stdin {
                // We want to ignore BrokenPipe here. A BrokenPipe indicates
                // either the hook is crashed/errored or it ran successfully.
                // Either way, this is an indication that the hook command
                // finished execution.  If the hook command was successful,
                // which we will check later in this function, we should not
                // fail this step here. We still want to check for all the other
                // error, in the case that the hook command is waiting for us to
                // write to stdin.
                // 我們想要在這裡忽略 BrokenPipe。BrokenPipe 表示 hook 已經崩潰/發生錯誤、或者已經成功執行
                // 無論如何,這代表了 hook 已經執行完畢。
                // 若 hook command 成功執行,後續在這個韓世忠會進行檢查,在這一步不應該讓他觸發失敗。
                // 我們仍然想要檢查所有其他錯誤,比如在 hook command 正在等待我們寫入標準輸入(stdin)的情況下。
                let state = format!("{{ \"pid\": {} }}", std::process::id());
                if let Err(e) = stdin.write_all(state.as_bytes()) {
                    if e.kind() != ErrorKind::BrokenPipe {
                        // Not a broken pipe. The hook command may be waiting
                        // for us.
                        // 不是 BrokenPipe 錯誤,可能是 hook command 正在等待我們寫入。
                        let _ = hook_process.kill();
                    }
                }
            }
            hook_process.wait()?;
        }
    }
    Ok(())
}

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

尚未有邦友留言

立即登入留言