From 558732d7002a1a5ae255212c6b2183e3e531ef4f Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Wed, 21 Jun 2023 18:56:55 +0000 Subject: [PATCH 01/18] implements can_handle in wasmtime executor. This commit implements can_handle in wasmtime executor. It checks if the entrypoint binary has wasm as the path extension. If not, the wasmtime executor will not be used. It adds a default executor to the wasmtime shim as a backup so it enables running wasm and containers side-by-side. Add wat extension support in can_handle Signed-off-by: jiaxiao zhou --- .../containerd-shim-wasmtime/src/executor.rs | 33 +++++++++++++++-- .../containerd-shim-wasmtime/src/instance.rs | 37 +++++++++++++++---- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/crates/containerd-shim-wasmtime/src/executor.rs b/crates/containerd-shim-wasmtime/src/executor.rs index 135ed1188..9c846e04b 100644 --- a/crates/containerd-shim-wasmtime/src/executor.rs +++ b/crates/containerd-shim-wasmtime/src/executor.rs @@ -1,5 +1,5 @@ use nix::unistd::{dup, dup2}; -use std::{fs::OpenOptions, os::fd::RawFd}; +use std::{fs::OpenOptions, os::fd::RawFd, path::PathBuf}; use anyhow::{anyhow, Result, Context}; use containerd_shim_wasm::sandbox::oci; @@ -39,8 +39,35 @@ impl Executor for WasmtimeExecutor { }; } - fn can_handle(&self, _spec: &Spec) -> bool { - true + fn can_handle(&self, spec: &Spec) -> bool { + // check if the entrypoint of the spec is a wasm binary. + let args = oci::get_args(spec); + if args.is_empty() { + return false; + } + + let start = args[0].clone(); + let mut iterator = start.split('#'); + let mut cmd = iterator.next().unwrap().to_string(); + let stripped = cmd.strip_prefix(std::path::MAIN_SEPARATOR); + if let Some(strpd) = stripped { + cmd = strpd.to_string(); + } + + let mut path = PathBuf::from(cmd); + if path.is_relative() { + path = std::env::current_dir().unwrap().join(path); + } + + // TODO: do we need to validate the wasm binary? + // ```rust + // let bytes = std::fs::read(path).unwrap(); + // wasmparser::validate(&bytes).is_ok() + // ``` + + path.extension() + .map(|ext| ext == "wasm" || ext == "wat") + .unwrap_or(false) } fn name(&self) -> &'static str { diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 5b5be474b..858b29327 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -4,6 +4,7 @@ use containerd_shim_wasm::sandbox::instance_utils::{ }; use libcontainer::container::builder::ContainerBuilder; use libcontainer::container::{Container, ContainerStatus}; +use libcontainer::workload::default::DefaultExecutor; use nix::errno::Errno; use nix::sys::wait::waitid; use serde::{Deserialize, Serialize}; @@ -19,7 +20,7 @@ use chrono::{DateTime, Utc}; use containerd_shim_wasm::sandbox::error::Error; use containerd_shim_wasm::sandbox::instance::Wait; use containerd_shim_wasm::sandbox::{EngineGetter, Instance, InstanceConfig}; -use libc::{dup2, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; +use libc::{dup, dup2, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libc::{SIGINT, SIGKILL}; use libcontainer::syscall::syscall::create_syscall; use log::error; @@ -243,13 +244,35 @@ impl Wasi { let stdout = maybe_open_stdio(stdout).context("could not open stdout")?; let stderr = maybe_open_stdio(stderr).context("could not open stderr")?; + let wasmtime_executor = Box::new(WasmtimeExecutor { + engine, + stdin, + stdout, + stderr, + }); + let default_executor = Box::::default(); + + if let Some(stdin) = stdin { + unsafe { + STDIN_FD = Some(dup(STDIN_FILENO)); + dup2(stdin, STDIN_FILENO); + } + } + if let Some(stdout) = stdout { + unsafe { + STDOUT_FD = Some(dup(STDOUT_FILENO)); + dup2(stdout, STDOUT_FILENO); + } + } + if let Some(stderr) = stderr { + unsafe { + STDERR_FD = Some(dup(STDERR_FILENO)); + dup2(stderr, STDERR_FILENO); + } + } + let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) - .with_executor(vec![Box::new(WasmtimeExecutor { - stdin, - stdout, - stderr, - engine, - })])? + .with_executor(vec![wasmtime_executor, default_executor])? .with_root_path(self.rootdir.clone())? .as_init(&self.bundle) .with_systemd(false) From 8b49b0c55a6fe63adc3a23b8979b3a1f02f6bc05 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 11 Jul 2023 21:38:55 +0000 Subject: [PATCH 02/18] test: add a python container to wasmtime shim for testing this commit adds a python container in the test directory. It also adds a few commands in the makefile to build the python flask app into a container, load into the kind cluster for testing. Eventually the wasmtime tests will have both python container and a wasm contaienr running in a pod. Signed-off-by: jiaxiao zhou --- Makefile | 8 +++++++- test/k8s/deploy.yaml | 3 +++ test/py-flask/Dockerfile | 18 ++++++++++++++++++ test/py-flask/app.py | 9 +++++++++ test/py-flask/requirements.txt | 1 + 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/py-flask/Dockerfile create mode 100644 test/py-flask/app.py create mode 100644 test/py-flask/requirements.txt diff --git a/Makefile b/Makefile index 9041b3e7d..4de964c30 100644 --- a/Makefile +++ b/Makefile @@ -67,10 +67,16 @@ bin/kind: test/k8s/Dockerfile test/k8s/_out/img: test/k8s/Dockerfile Cargo.toml Cargo.lock $(shell find . -type f -name '*.rs') mkdir -p $(@D) && $(DOCKER_BUILD) -f test/k8s/Dockerfile --iidfile=$(@) --load . +.PHONY: test/py-flask +test/py-flask: + docker build -t py-flask-app:latest -f $@/Dockerfile $@ + mkdir -p $@/out && docker save -o $@/out/img.tar py-flask-app:latest + .PHONY: test/k8s/cluster -test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind +test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind test/py-flask bin/kind create cluster --name $(KIND_CLUSTER_NAME) --image="$(shell cat test/k8s/_out/img)" && \ bin/kind load image-archive --name $(KIND_CLUSTER_NAME) $(<) + bin/kind load image-archive --name $(KIND_CLUSTER_NAME) test/py-flask/out/img.tar .PHONY: test/k8s test/k8s: test/k8s/cluster diff --git a/test/k8s/deploy.yaml b/test/k8s/deploy.yaml index 91f59edbf..2f6bb9bc0 100644 --- a/test/k8s/deploy.yaml +++ b/test/k8s/deploy.yaml @@ -24,4 +24,7 @@ spec: containers: - name: demo image: ghcr.io/containerd/runwasi/wasi-demo-app:latest + imagePullPolicy: Never + - name: py-demo + image: py-cmd-app:v1 imagePullPolicy: Never \ No newline at end of file diff --git a/test/py-flask/Dockerfile b/test/py-flask/Dockerfile new file mode 100644 index 000000000..c04dd0123 --- /dev/null +++ b/test/py-flask/Dockerfile @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1.4 +FROM --platform=$BUILDPLATFORM python:3.10-alpine AS builder + +WORKDIR /app + +COPY requirements.txt /app +RUN --mount=type=cache,target=/root/.cache/pip \ + pip3 install -r requirements.txt + +COPY . /app + +ENTRYPOINT ["python3"] +CMD ["app.py"] + +FROM builder as dev-envs + +RUN apk update && \ + apk add git diff --git a/test/py-flask/app.py b/test/py-flask/app.py new file mode 100644 index 000000000..4a8d043b6 --- /dev/null +++ b/test/py-flask/app.py @@ -0,0 +1,9 @@ +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello(): + return "Hello World!" + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8000) \ No newline at end of file diff --git a/test/py-flask/requirements.txt b/test/py-flask/requirements.txt new file mode 100644 index 000000000..8ab6294c6 --- /dev/null +++ b/test/py-flask/requirements.txt @@ -0,0 +1 @@ +flask \ No newline at end of file From bf7102932a77daeb46ea690f75f2da9ab0644afd Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Wed, 12 Jul 2023 20:46:20 +0000 Subject: [PATCH 03/18] feat: add docker buildx to CI Signed-off-by: jiaxiao zhou --- .github/workflows/ci.yml | 7 +++++++ Makefile | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3679e2c86..33ddb56e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,13 @@ jobs: run: | sudo apt -y update sudo apt install -y pkg-config libsystemd-dev libdbus-glib-1-dev build-essential libelf-dev libseccomp-dev libclang-dev + - + # Add support for more platforms with QEMU (optional) + # https://github.com/docker/setup-qemu-action + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - name: run run: | make test/k8s diff --git a/Makefile b/Makefile index 4de964c30..f6ba6fd48 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ test/k8s/_out/img: test/k8s/Dockerfile Cargo.toml Cargo.lock $(shell find . -typ .PHONY: test/py-flask test/py-flask: - docker build -t py-flask-app:latest -f $@/Dockerfile $@ + $(DOCKER_BUILD) -t py-flask-app:latest -f $@/Dockerfile $@ mkdir -p $@/out && docker save -o $@/out/img.tar py-flask-app:latest .PHONY: test/k8s/cluster From 2827e2a57428e860637f3bca1b3cb868134eb772 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Wed, 12 Jul 2023 22:28:58 +0000 Subject: [PATCH 04/18] add --load to test/py-flask to fix the CI issue The issue says "WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load" Signed-off-by: jiaxiao zhou --- Makefile | 2 +- test/k8s/deploy.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f6ba6fd48..419c79b4e 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ test/k8s/_out/img: test/k8s/Dockerfile Cargo.toml Cargo.lock $(shell find . -typ .PHONY: test/py-flask test/py-flask: - $(DOCKER_BUILD) -t py-flask-app:latest -f $@/Dockerfile $@ + $(DOCKER_BUILD) -t py-flask-app:latest -f $@/Dockerfile --load $@ mkdir -p $@/out && docker save -o $@/out/img.tar py-flask-app:latest .PHONY: test/k8s/cluster diff --git a/test/k8s/deploy.yaml b/test/k8s/deploy.yaml index 2f6bb9bc0..eab1bb6bb 100644 --- a/test/k8s/deploy.yaml +++ b/test/k8s/deploy.yaml @@ -26,5 +26,5 @@ spec: image: ghcr.io/containerd/runwasi/wasi-demo-app:latest imagePullPolicy: Never - name: py-demo - image: py-cmd-app:v1 + image: py-flask-app:latest imagePullPolicy: Never \ No newline at end of file From bde59f032fc5d4ff90cbf28117126a72a9d69f11 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Mon, 31 Jul 2023 23:22:40 +0000 Subject: [PATCH 05/18] feat: removed py-flask image and use hello-world image from docker.io Signed-off-by: jiaxiao zhou --- Makefile | 12 ++++++------ .../containerd-shim-wasmtime/src/instance.rs | 1 + test/k8s/deploy.yaml | 4 ++-- test/py-flask/Dockerfile | 18 ------------------ test/py-flask/app.py | 9 --------- test/py-flask/requirements.txt | 1 - 6 files changed, 9 insertions(+), 36 deletions(-) delete mode 100644 test/py-flask/Dockerfile delete mode 100644 test/py-flask/app.py delete mode 100644 test/py-flask/requirements.txt diff --git a/Makefile b/Makefile index 419c79b4e..b6e32d1c0 100644 --- a/Makefile +++ b/Makefile @@ -67,16 +67,16 @@ bin/kind: test/k8s/Dockerfile test/k8s/_out/img: test/k8s/Dockerfile Cargo.toml Cargo.lock $(shell find . -type f -name '*.rs') mkdir -p $(@D) && $(DOCKER_BUILD) -f test/k8s/Dockerfile --iidfile=$(@) --load . -.PHONY: test/py-flask -test/py-flask: - $(DOCKER_BUILD) -t py-flask-app:latest -f $@/Dockerfile --load $@ - mkdir -p $@/out && docker save -o $@/out/img.tar py-flask-app:latest +.PHONY: test/hello-world +test/hello-world: + docker pull docker.io/hello-world:latest + mkdir -p $@/out && docker save -o $@/out/img.tar docker.io/hello-world:latest .PHONY: test/k8s/cluster -test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind test/py-flask +test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind test/hello-world bin/kind create cluster --name $(KIND_CLUSTER_NAME) --image="$(shell cat test/k8s/_out/img)" && \ bin/kind load image-archive --name $(KIND_CLUSTER_NAME) $(<) - bin/kind load image-archive --name $(KIND_CLUSTER_NAME) test/py-flask/out/img.tar + bin/kind load image-archive --name $(KIND_CLUSTER_NAME) test/hello-world/out/img.tar .PHONY: test/k8s test/k8s: test/k8s/cluster diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 858b29327..445ab48c2 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -252,6 +252,7 @@ impl Wasi { }); let default_executor = Box::::default(); + // these are used by the default container runtime to redirect stdio if let Some(stdin) = stdin { unsafe { STDIN_FD = Some(dup(STDIN_FILENO)); diff --git a/test/k8s/deploy.yaml b/test/k8s/deploy.yaml index eab1bb6bb..21a6d8bfe 100644 --- a/test/k8s/deploy.yaml +++ b/test/k8s/deploy.yaml @@ -25,6 +25,6 @@ spec: - name: demo image: ghcr.io/containerd/runwasi/wasi-demo-app:latest imagePullPolicy: Never - - name: py-demo - image: py-flask-app:latest + - name: hello-world + image: hello-world:latest imagePullPolicy: Never \ No newline at end of file diff --git a/test/py-flask/Dockerfile b/test/py-flask/Dockerfile deleted file mode 100644 index c04dd0123..000000000 --- a/test/py-flask/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -# syntax=docker/dockerfile:1.4 -FROM --platform=$BUILDPLATFORM python:3.10-alpine AS builder - -WORKDIR /app - -COPY requirements.txt /app -RUN --mount=type=cache,target=/root/.cache/pip \ - pip3 install -r requirements.txt - -COPY . /app - -ENTRYPOINT ["python3"] -CMD ["app.py"] - -FROM builder as dev-envs - -RUN apk update && \ - apk add git diff --git a/test/py-flask/app.py b/test/py-flask/app.py deleted file mode 100644 index 4a8d043b6..000000000 --- a/test/py-flask/app.py +++ /dev/null @@ -1,9 +0,0 @@ -from flask import Flask -app = Flask(__name__) - -@app.route('/') -def hello(): - return "Hello World!" - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=8000) \ No newline at end of file diff --git a/test/py-flask/requirements.txt b/test/py-flask/requirements.txt deleted file mode 100644 index 8ab6294c6..000000000 --- a/test/py-flask/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flask \ No newline at end of file From e24adc0e8b64770146fcc82a6070d2cd3934bf81 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Mon, 31 Jul 2023 23:26:37 +0000 Subject: [PATCH 06/18] refactor: move reset_stdio method to wasitest module Signed-off-by: jiaxiao zhou --- .../containerd-shim-wasmtime/src/instance.rs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 445ab48c2..f86a24278 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -52,20 +52,6 @@ pub struct Wasi { id: String, } -pub fn reset_stdio() { - unsafe { - if STDIN_FD.is_some() { - dup2(STDIN_FD.unwrap(), STDIN_FILENO); - } - if STDOUT_FD.is_some() { - dup2(STDOUT_FD.unwrap(), STDOUT_FILENO); - } - if STDERR_FD.is_some() { - dup2(STDERR_FD.unwrap(), STDERR_FILENO); - } - } -} - #[derive(Serialize, Deserialize)] struct Options { root: Option, @@ -432,4 +418,18 @@ mod wasitest { .set_stderr(dir.path().join("stderr").to_str().unwrap().to_string()); Ok(cfg.to_owned()) } + + fn reset_stdio() { + unsafe { + if STDIN_FD.is_some() { + dup2(STDIN_FD.unwrap(), STDIN_FILENO); + } + if STDOUT_FD.is_some() { + dup2(STDOUT_FD.unwrap(), STDOUT_FILENO); + } + if STDERR_FD.is_some() { + dup2(STDERR_FD.unwrap(), STDERR_FILENO); + } + } + } } From 0f932336fe7c3b55e511f16cbad0701a4bd624f3 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 1 Aug 2023 21:39:55 +0000 Subject: [PATCH 07/18] fix: changed the hello-world image to nginx for testing in k8s Signed-off-by: jiaxiao zhou --- Makefile | 12 ++++++------ test/k8s/deploy.yaml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 19b1f8d42..9b9bc799a 100644 --- a/Makefile +++ b/Makefile @@ -67,16 +67,16 @@ bin/kind: test/k8s/Dockerfile test/k8s/_out/img: test/k8s/Dockerfile Cargo.toml Cargo.lock $(shell find . -type f -name '*.rs') mkdir -p $(@D) && $(DOCKER_BUILD) -f test/k8s/Dockerfile --iidfile=$(@) --load . -.PHONY: test/hello-world -test/hello-world: - docker pull docker.io/hello-world:latest - mkdir -p $@/out && docker save -o $@/out/img.tar docker.io/hello-world:latest +.PHONY: test/nginx +test/nginx: + docker pull docker.io/nginx:latest + mkdir -p $@/out && docker save -o $@/out/img.tar docker.io/nginx:latest .PHONY: test/k8s/cluster -test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind test/hello-world +test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind test/nginx bin/kind create cluster --name $(KIND_CLUSTER_NAME) --image="$(shell cat test/k8s/_out/img)" && \ bin/kind load image-archive --name $(KIND_CLUSTER_NAME) $(<) - bin/kind load image-archive --name $(KIND_CLUSTER_NAME) test/hello-world/out/img.tar + bin/kind load image-archive --name $(KIND_CLUSTER_NAME) test/nginx/out/img.tar .PHONY: test/k8s test/k8s: test/k8s/cluster diff --git a/test/k8s/deploy.yaml b/test/k8s/deploy.yaml index 21a6d8bfe..ccaf2328b 100644 --- a/test/k8s/deploy.yaml +++ b/test/k8s/deploy.yaml @@ -25,6 +25,6 @@ spec: - name: demo image: ghcr.io/containerd/runwasi/wasi-demo-app:latest imagePullPolicy: Never - - name: hello-world - image: hello-world:latest + - name: nginx + image: nginx:latest imagePullPolicy: Never \ No newline at end of file From 6a86100fea66b68d4389b2e78b958fbde9622079 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 1 Aug 2023 22:29:05 +0000 Subject: [PATCH 08/18] feat: implement can_handle for default executor Signed-off-by: jiaxiao zhou --- .../src/container_executor.rs | 147 ++++++++++++++++++ .../containerd-shim-wasmtime/src/instance.rs | 51 +++--- crates/containerd-shim-wasmtime/src/lib.rs | 1 + 3 files changed, 166 insertions(+), 33 deletions(-) create mode 100644 crates/containerd-shim-wasmtime/src/container_executor.rs diff --git a/crates/containerd-shim-wasmtime/src/container_executor.rs b/crates/containerd-shim-wasmtime/src/container_executor.rs new file mode 100644 index 000000000..61eee08f1 --- /dev/null +++ b/crates/containerd-shim-wasmtime/src/container_executor.rs @@ -0,0 +1,147 @@ +use libcontainer::workload::{Executor, ExecutorError, EMPTY}; +use nix::unistd::{dup, dup2}; + +use std::ffi::CString; +use std::io::Read; +use std::{fs::OpenOptions, os::fd::RawFd, path::PathBuf}; + +use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; +use nix::unistd; +use oci_spec::runtime::Spec; + +const EXECUTOR_NAME: &str = "default"; + +#[derive(Default)] +pub struct DefaultExecutor { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +impl Executor for DefaultExecutor { + fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> { + log::debug!("executing workload with default handler"); + let args = spec + .process() + .as_ref() + .and_then(|p| p.args().as_ref()) + .unwrap_or(&EMPTY); + + if args.is_empty() { + log::error!("no arguments provided to execute"); + Err(ExecutorError::InvalidArg)?; + } + + redirect_io(self.stdin, self.stdout, self.stderr).map_err(|err| { + log::error!("failed to redirect io: {}", err); + ExecutorError::Other(format!("failed to redirect io: {}", err)) + })?; + + let executable = args[0].as_str(); + let cstring_path = CString::new(executable.as_bytes()).map_err(|err| { + log::error!("failed to convert path {executable:?} to cstring: {}", err,); + ExecutorError::InvalidArg + })?; + let a: Vec = args + .iter() + .map(|s| CString::new(s.as_bytes()).unwrap_or_default()) + .collect(); + unistd::execvp(&cstring_path, &a).map_err(|err| { + log::error!("failed to execvp: {}", err); + ExecutorError::Execution(err.into()) + })?; + + // After execvp is called, the process is replaced with the container + // payload through execvp, so it should never reach here. + unreachable!(); + } + + fn can_handle(&self, spec: &Spec) -> bool { + let args = spec + .process() + .as_ref() + .and_then(|p| p.args().as_ref()) + .unwrap_or(&EMPTY); + + if args.is_empty() { + return false; + } + + let executable = args[0].as_str(); + + // mostly follows youki's verify_binary implementation + // https://github.com/containers/youki/blob/2d6fd7650bb0f22a78fb5fa982b5628f61fe25af/crates/libcontainer/src/process/container_init_process.rs#L106 + let path = if executable.contains('/') { + PathBuf::from(executable) + } else { + let path = std::env::var("PATH").unwrap_or_default(); + // check each path in $PATH + let mut found = false; + let mut found_path = PathBuf::default(); + for p in path.split(':') { + let path = PathBuf::from(p).join(executable); + if path.exists() { + found = true; + found_path = path; + break; + } + } + if !found { + return false; + } + found_path + }; + + // check execute permission + use std::os::unix::fs::PermissionsExt; + let metadata = path.metadata(); + if metadata.is_err() { + log::info!("failed to get metadata of {:?}", path); + return false; + } + let metadata = metadata.unwrap(); + let permissions = metadata.permissions(); + if !metadata.is_file() || permissions.mode() & 0o001 == 0 { + log::info!("{} is not a file or has no execute permission", executable); + return false; + } + + // check the ELF magic number + // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header + let mut buffer = [0; 4]; + let file = OpenOptions::new().read(true).open(path); + if file.is_err() { + log::info!("failed to open {}", executable); + return false; + } + let mut file = file.unwrap(); + match file.read_exact(&mut buffer) { + Ok(_) => {} + Err(err) => { + log::info!("failed to read magic number of {}: {}", executable, err); + return false; + } + } + buffer == [0x7f, 0x45, 0x4c, 0x46] + } + + fn name(&self) -> &'static str { + EXECUTOR_NAME + } +} + +fn redirect_io(stdin: Option, stdout: Option, stderr: Option) -> anyhow::Result<()> { + if let Some(stdin) = stdin { + dup(STDIN_FILENO)?; + dup2(stdin, STDIN_FILENO)?; + } + if let Some(stdout) = stdout { + dup(STDOUT_FILENO)?; + dup2(stdout, STDOUT_FILENO)?; + } + if let Some(stderr) = stderr { + dup(STDERR_FILENO)?; + dup2(stderr, STDERR_FILENO)?; + } + Ok(()) +} diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 6c990e1a1..e1bec5a3e 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -4,13 +4,11 @@ use containerd_shim_wasm::sandbox::instance_utils::{ }; use libcontainer::container::builder::ContainerBuilder; use libcontainer::container::{Container, ContainerStatus}; -use libcontainer::workload::default::DefaultExecutor; use nix::errno::Errno; use nix::sys::wait::waitid; use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::{ErrorKind, Read}; -use std::os::fd::RawFd; use std::path::{Path, PathBuf}; use std::sync::{Arc, Condvar, Mutex}; use std::thread; @@ -20,7 +18,6 @@ use chrono::{DateTime, Utc}; use containerd_shim_wasm::sandbox::error::Error; use containerd_shim_wasm::sandbox::instance::Wait; use containerd_shim_wasm::sandbox::{EngineGetter, Instance, InstanceConfig}; -use libc::{dup, dup2, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libc::{SIGINT, SIGKILL}; use libcontainer::syscall::syscall::create_syscall; use log::error; @@ -28,16 +25,13 @@ use nix::sys::wait::{Id as WaitID, WaitPidFlag, WaitStatus}; use wasmtime::Engine; +use crate::container_executor::DefaultExecutor; use crate::executor::WasmtimeExecutor; use libcontainer::signal::Signal; static DEFAULT_CONTAINER_ROOT_DIR: &str = "/run/containerd/wasmtime"; type ExitCode = Arc<(Mutex)>>, Condvar)>; -static mut STDIN_FD: Option = None; -static mut STDOUT_FD: Option = None; -static mut STDERR_FD: Option = None; - pub struct Wasi { exit_code: ExitCode, engine: wasmtime::Engine, @@ -233,33 +227,17 @@ impl Wasi { stdout, stderr, }); - let default_executor = Box::::default(); - - // these are used by the default container runtime to redirect stdio - if let Some(stdin) = stdin { - unsafe { - STDIN_FD = Some(dup(STDIN_FILENO)); - dup2(stdin, STDIN_FILENO); - } - } - if let Some(stdout) = stdout { - unsafe { - STDOUT_FD = Some(dup(STDOUT_FILENO)); - dup2(stdout, STDOUT_FILENO); - } - } - if let Some(stderr) = stderr { - unsafe { - STDERR_FD = Some(dup(STDERR_FILENO)); - dup2(stderr, STDERR_FILENO); - } - } - + let default_executor = Box::new(DefaultExecutor { + stdin, + stdout, + stderr, + }); let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) - .with_executor(vec![wasmtime_executor, default_executor])? + .with_executor(vec![default_executor, wasmtime_executor])? .with_root_path(self.rootdir.clone())? .as_init(&self.bundle) .with_systemd(false) + .with_detach(true) .build()?; Ok(container) } @@ -276,6 +254,7 @@ impl EngineGetter for Wasi { mod wasitest { use std::fs::{create_dir, read_to_string, File, OpenOptions}; use std::io::prelude::*; + use std::os::fd::RawFd; use std::os::unix::prelude::OpenOptionsExt; use std::sync::mpsc::channel; use std::time::Duration; @@ -284,9 +263,15 @@ mod wasitest { use containerd_shim_wasm::sandbox::exec::has_cap_sys_admin; use containerd_shim_wasm::sandbox::instance::Wait; use containerd_shim_wasm::sandbox::testutil::run_test_with_sudo; + use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; + use nix::unistd::dup2; use oci_spec::runtime::{ProcessBuilder, RootBuilder, SpecBuilder}; use tempfile::{tempdir, TempDir}; + static mut STDIN_FD: Option = None; + static mut STDOUT_FD: Option = None; + static mut STDERR_FD: Option = None; + use super::*; // This is taken from https://github.com/bytecodealliance/wasmtime/blob/6a60e8363f50b936e4c4fc958cb9742314ff09f3/docs/WASI-tutorial.md?plain=1#L270-L298 @@ -423,13 +408,13 @@ mod wasitest { fn reset_stdio() { unsafe { if STDIN_FD.is_some() { - dup2(STDIN_FD.unwrap(), STDIN_FILENO); + let _ = dup2(STDIN_FD.unwrap(), STDIN_FILENO); } if STDOUT_FD.is_some() { - dup2(STDOUT_FD.unwrap(), STDOUT_FILENO); + let _ = dup2(STDOUT_FD.unwrap(), STDOUT_FILENO); } if STDERR_FD.is_some() { - dup2(STDERR_FD.unwrap(), STDERR_FILENO); + let _ = dup2(STDERR_FD.unwrap(), STDERR_FILENO); } } } diff --git a/crates/containerd-shim-wasmtime/src/lib.rs b/crates/containerd-shim-wasmtime/src/lib.rs index 312731e44..8454e970d 100644 --- a/crates/containerd-shim-wasmtime/src/lib.rs +++ b/crates/containerd-shim-wasmtime/src/lib.rs @@ -1,3 +1,4 @@ +mod container_executor; pub mod error; pub mod executor; pub mod instance; From 01d4a648ff8ee1b19729621ce7121f5162fec932 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 1 Aug 2023 22:35:18 +0000 Subject: [PATCH 09/18] refactor: restructure the files. adding a new executors mod Signed-off-by: jiaxiao zhou --- .../src/{container_executor.rs => executors/container.rs} | 4 ++-- crates/containerd-shim-wasmtime/src/executors/mod.rs | 8 ++++++++ .../src/{executor.rs => executors/wasi.rs} | 0 crates/containerd-shim-wasmtime/src/instance.rs | 6 +++--- crates/containerd-shim-wasmtime/src/lib.rs | 3 +-- 5 files changed, 14 insertions(+), 7 deletions(-) rename crates/containerd-shim-wasmtime/src/{container_executor.rs => executors/container.rs} (98%) create mode 100644 crates/containerd-shim-wasmtime/src/executors/mod.rs rename crates/containerd-shim-wasmtime/src/{executor.rs => executors/wasi.rs} (100%) diff --git a/crates/containerd-shim-wasmtime/src/container_executor.rs b/crates/containerd-shim-wasmtime/src/executors/container.rs similarity index 98% rename from crates/containerd-shim-wasmtime/src/container_executor.rs rename to crates/containerd-shim-wasmtime/src/executors/container.rs index 61eee08f1..136e6bb1d 100644 --- a/crates/containerd-shim-wasmtime/src/container_executor.rs +++ b/crates/containerd-shim-wasmtime/src/executors/container.rs @@ -12,13 +12,13 @@ use oci_spec::runtime::Spec; const EXECUTOR_NAME: &str = "default"; #[derive(Default)] -pub struct DefaultExecutor { +pub struct LinuxContainerExecutor { pub stdin: Option, pub stdout: Option, pub stderr: Option, } -impl Executor for DefaultExecutor { +impl Executor for LinuxContainerExecutor { fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> { log::debug!("executing workload with default handler"); let args = spec diff --git a/crates/containerd-shim-wasmtime/src/executors/mod.rs b/crates/containerd-shim-wasmtime/src/executors/mod.rs new file mode 100644 index 000000000..54a85e769 --- /dev/null +++ b/crates/containerd-shim-wasmtime/src/executors/mod.rs @@ -0,0 +1,8 @@ +//! This module contains the `WasmtimeExecutor` and `LinuxContainerExecutor`. +//! + +pub mod container; +pub mod wasi; + +pub use container::LinuxContainerExecutor; +pub use wasi::WasmtimeExecutor; diff --git a/crates/containerd-shim-wasmtime/src/executor.rs b/crates/containerd-shim-wasmtime/src/executors/wasi.rs similarity index 100% rename from crates/containerd-shim-wasmtime/src/executor.rs rename to crates/containerd-shim-wasmtime/src/executors/wasi.rs diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index e1bec5a3e..bd93313f5 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -25,10 +25,10 @@ use nix::sys::wait::{Id as WaitID, WaitPidFlag, WaitStatus}; use wasmtime::Engine; -use crate::container_executor::DefaultExecutor; -use crate::executor::WasmtimeExecutor; use libcontainer::signal::Signal; +use crate::executors::{LinuxContainerExecutor, WasmtimeExecutor}; + static DEFAULT_CONTAINER_ROOT_DIR: &str = "/run/containerd/wasmtime"; type ExitCode = Arc<(Mutex)>>, Condvar)>; @@ -227,7 +227,7 @@ impl Wasi { stdout, stderr, }); - let default_executor = Box::new(DefaultExecutor { + let default_executor = Box::new(LinuxContainerExecutor { stdin, stdout, stderr, diff --git a/crates/containerd-shim-wasmtime/src/lib.rs b/crates/containerd-shim-wasmtime/src/lib.rs index 8454e970d..9cb60e81a 100644 --- a/crates/containerd-shim-wasmtime/src/lib.rs +++ b/crates/containerd-shim-wasmtime/src/lib.rs @@ -1,5 +1,4 @@ -mod container_executor; pub mod error; -pub mod executor; +pub mod executors; pub mod instance; pub mod oci_wasmtime; From eb9f6e94263dfdfd1148f2b2add67440ed773ff9 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 1 Aug 2023 23:02:06 +0000 Subject: [PATCH 10/18] fix: removed the extra lines in CI Signed-off-by: jiaxiao zhou --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94b36d806..e32e911cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,13 +64,6 @@ jobs: - name: Setup OCI runtime build env run: ${GITHUB_WORKSPACE}/.github/scripts/build.sh shell: bash - - - # Add support for more platforms with QEMU (optional) - # https://github.com/docker/setup-qemu-action - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - name: run run: make test/k8s # only runs when the previous step fails From ef1b387bac249c5c18129ed6a5fe7097bf2d0154 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 1 Aug 2023 23:04:05 +0000 Subject: [PATCH 11/18] fix: fetch nginx from docker.io in k8s Signed-off-by: jiaxiao zhou --- Makefile | 3 +-- test/k8s/deploy.yaml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 9b9bc799a..b7ecc70dd 100644 --- a/Makefile +++ b/Makefile @@ -73,10 +73,9 @@ test/nginx: mkdir -p $@/out && docker save -o $@/out/img.tar docker.io/nginx:latest .PHONY: test/k8s/cluster -test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind test/nginx +test/k8s/cluster: target/wasm32-wasi/$(TARGET)/img.tar bin/kind test/k8s/_out/img bin/kind bin/kind create cluster --name $(KIND_CLUSTER_NAME) --image="$(shell cat test/k8s/_out/img)" && \ bin/kind load image-archive --name $(KIND_CLUSTER_NAME) $(<) - bin/kind load image-archive --name $(KIND_CLUSTER_NAME) test/nginx/out/img.tar .PHONY: test/k8s test/k8s: test/k8s/cluster diff --git a/test/k8s/deploy.yaml b/test/k8s/deploy.yaml index ccaf2328b..2d940af4f 100644 --- a/test/k8s/deploy.yaml +++ b/test/k8s/deploy.yaml @@ -26,5 +26,5 @@ spec: image: ghcr.io/containerd/runwasi/wasi-demo-app:latest imagePullPolicy: Never - name: nginx - image: nginx:latest + image: docker.io/nginx:latest imagePullPolicy: Never \ No newline at end of file From 2f07ec51aef28e19dc1b64e34957b029b0b97f7f Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Tue, 1 Aug 2023 23:29:06 +0000 Subject: [PATCH 12/18] fix: remove never pull policy from k8s Signed-off-by: jiaxiao zhou --- test/k8s/deploy.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/k8s/deploy.yaml b/test/k8s/deploy.yaml index 2d940af4f..64d0de157 100644 --- a/test/k8s/deploy.yaml +++ b/test/k8s/deploy.yaml @@ -27,4 +27,5 @@ spec: imagePullPolicy: Never - name: nginx image: docker.io/nginx:latest - imagePullPolicy: Never \ No newline at end of file + ports: + - containerPort: 80 \ No newline at end of file From 70668d5a83748a82693d43660623be58e739d272 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Wed, 2 Aug 2023 00:18:55 +0000 Subject: [PATCH 13/18] feat: implement check for shebang in the linux executor Signed-off-by: jiaxiao zhou --- .../src/executors/container.rs | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/crates/containerd-shim-wasmtime/src/executors/container.rs b/crates/containerd-shim-wasmtime/src/executors/container.rs index 136e6bb1d..9f0018879 100644 --- a/crates/containerd-shim-wasmtime/src/executors/container.rs +++ b/crates/containerd-shim-wasmtime/src/executors/container.rs @@ -106,23 +106,13 @@ impl Executor for LinuxContainerExecutor { return false; } - // check the ELF magic number + // check the shebang and ELF magic number // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - let mut buffer = [0; 4]; - let file = OpenOptions::new().read(true).open(path); - if file.is_err() { - log::info!("failed to open {}", executable); + if !check_shebang(path.clone(), executable) && !check_elf(path.clone(), executable) { + log::info!("{} is not a valid script or elf file", executable); return false; } - let mut file = file.unwrap(); - match file.read_exact(&mut buffer) { - Ok(_) => {} - Err(err) => { - log::info!("failed to read magic number of {}: {}", executable, err); - return false; - } - } - buffer == [0x7f, 0x45, 0x4c, 0x46] + true } fn name(&self) -> &'static str { @@ -145,3 +135,41 @@ fn redirect_io(stdin: Option, stdout: Option, stderr: Option) -> } Ok(()) } + +fn check_shebang(path: PathBuf, executable: &str) -> bool { + let mut buffer = [0; 2]; + + let file = OpenOptions::new().read(true).open(path); + if file.is_err() { + log::info!("failed to open {}", executable); + return false; + } + let mut file = file.unwrap(); + match file.read_exact(&mut buffer) { + Ok(_) => {} + Err(err) => { + log::info!("failed to read shebang of {}: {}", executable, err); + return false; + } + } + buffer == [0x23, 0x21] // #! +} + +fn check_elf(path: PathBuf, executable: &str) -> bool { + let mut buffer = [0; 4]; + + let file = OpenOptions::new().read(true).open(path); + if file.is_err() { + log::info!("failed to open {}", executable); + return false; + } + let mut file = file.unwrap(); + match file.read_exact(&mut buffer) { + Ok(_) => {} + Err(err) => { + log::info!("failed to read shebang of {}: {}", executable, err); + return false; + } + } + buffer == [0x7f, 0x45, 0x4c, 0x46] // ELF magic number +} From 71166340971bb44f88400e4ad5af80960a2866eb Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Wed, 2 Aug 2023 00:44:51 +0000 Subject: [PATCH 14/18] isolate test/k3s deploy.yaml file since wasmedge doesn't have default executor Signed-off-by: jiaxiao zhou --- Makefile | 2 +- test/k3s/deploy.yaml | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/k3s/deploy.yaml diff --git a/Makefile b/Makefile index b7ecc70dd..b9c7dd728 100644 --- a/Makefile +++ b/Makefile @@ -108,7 +108,7 @@ test/k3s: target/wasm32-wasi/$(TARGET)/img.tar bin/k3s sudo systemctl restart k3s-runwasi && \ timeout 60 bash -c -- 'while true; do sudo bin/k3s ctr version && break; sleep 1; done' && \ sudo bin/k3s ctr image import --all-platforms target/wasm32-wasi/$(TARGET)/img.tar && \ - sudo bin/k3s kubectl apply -f test/k8s/deploy.yaml + sudo bin/k3s kubectl apply -f test/k3s/deploy.yaml sudo bin/k3s kubectl wait deployment wasi-demo --for condition=Available=True --timeout=90s && \ sudo bin/k3s kubectl get pods -o wide diff --git a/test/k3s/deploy.yaml b/test/k3s/deploy.yaml new file mode 100644 index 000000000..41c51fbd3 --- /dev/null +++ b/test/k3s/deploy.yaml @@ -0,0 +1,27 @@ +apiVersion: node.k8s.io/v1 +kind: RuntimeClass +metadata: + name: wasm +handler: wasm +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: wasi-demo + labels: + app: wasi-demo +spec: + replicas: 3 + selector: + matchLabels: + app: wasi-demo + template: + metadata: + labels: + app: wasi-demo + spec: + runtimeClassName: wasm + containers: + - name: demo + image: ghcr.io/containerd/runwasi/wasi-demo-app:latest + imagePullPolicy: Never From a5dd8be7141aebc48b89d0b238e23f727031685d Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Fri, 4 Aug 2023 21:04:28 +0000 Subject: [PATCH 15/18] simplify the logic in verifying ELF file and shebang and apply review comments Signed-off-by: jiaxiao zhou --- .../src/executors/container.rs | 71 +++++++------------ .../src/executors/wasi.rs | 4 +- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/crates/containerd-shim-wasmtime/src/executors/container.rs b/crates/containerd-shim-wasmtime/src/executors/container.rs index 9f0018879..fc9506ed9 100644 --- a/crates/containerd-shim-wasmtime/src/executors/container.rs +++ b/crates/containerd-shim-wasmtime/src/executors/container.rs @@ -1,3 +1,4 @@ +use containerd_shim_wasm::sandbox::oci; use libcontainer::workload::{Executor, ExecutorError, EMPTY}; use nix::unistd::{dup, dup2}; @@ -57,11 +58,7 @@ impl Executor for LinuxContainerExecutor { } fn can_handle(&self, spec: &Spec) -> bool { - let args = spec - .process() - .as_ref() - .and_then(|p| p.args().as_ref()) - .unwrap_or(&EMPTY); + let args = oci::get_args(spec); if args.is_empty() { return false; @@ -108,11 +105,31 @@ impl Executor for LinuxContainerExecutor { // check the shebang and ELF magic number // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - if !check_shebang(path.clone(), executable) && !check_elf(path.clone(), executable) { - log::info!("{} is not a valid script or elf file", executable); + let mut buffer = [0; 4]; + + let file = OpenOptions::new().read(true).open(path); + if file.is_err() { + log::info!("failed to open {}", executable); return false; } - true + let mut file = file.unwrap(); + match file.read_exact(&mut buffer) { + Ok(_) => {} + Err(err) => { + log::info!("failed to read shebang of {}: {}", executable, err); + return false; + } + } + match buffer { + // ELF magic number + [0x7f, 0x45, 0x4c, 0x46] => true, + // shebang + [0x23, 0x21, ..] => true, + _ => { + log::info!("{} is not a valid script or elf file", executable); + false + } + } } fn name(&self) -> &'static str { @@ -135,41 +152,3 @@ fn redirect_io(stdin: Option, stdout: Option, stderr: Option) -> } Ok(()) } - -fn check_shebang(path: PathBuf, executable: &str) -> bool { - let mut buffer = [0; 2]; - - let file = OpenOptions::new().read(true).open(path); - if file.is_err() { - log::info!("failed to open {}", executable); - return false; - } - let mut file = file.unwrap(); - match file.read_exact(&mut buffer) { - Ok(_) => {} - Err(err) => { - log::info!("failed to read shebang of {}: {}", executable, err); - return false; - } - } - buffer == [0x23, 0x21] // #! -} - -fn check_elf(path: PathBuf, executable: &str) -> bool { - let mut buffer = [0; 4]; - - let file = OpenOptions::new().read(true).open(path); - if file.is_err() { - log::info!("failed to open {}", executable); - return false; - } - let mut file = file.unwrap(); - match file.read_exact(&mut buffer) { - Ok(_) => {} - Err(err) => { - log::info!("failed to read shebang of {}: {}", executable, err); - return false; - } - } - buffer == [0x7f, 0x45, 0x4c, 0x46] // ELF magic number -} diff --git a/crates/containerd-shim-wasmtime/src/executors/wasi.rs b/crates/containerd-shim-wasmtime/src/executors/wasi.rs index f87a041df..1f26a0095 100644 --- a/crates/containerd-shim-wasmtime/src/executors/wasi.rs +++ b/crates/containerd-shim-wasmtime/src/executors/wasi.rs @@ -66,8 +66,8 @@ impl Executor for WasmtimeExecutor { // ``` path.extension() - .map(|ext| ext == "wasm" || ext == "wat") - .unwrap_or(false) + .map(|ext| ext.to_ascii_lowercase()) + .is_some_and(|ext| ext == "wasm" || ext == "wat") } fn name(&self) -> &'static str { From f46aa5f1e90c8282dfb09f2f832dcde73f6d62dd Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Fri, 4 Aug 2023 21:12:51 +0000 Subject: [PATCH 16/18] simplify the can_handle logic in wasi executor Signed-off-by: jiaxiao zhou --- .../src/executors/container.rs | 71 ++----------------- .../src/executors/wasi.rs | 12 +--- .../containerd-shim-wasmtime/src/instance.rs | 6 +- 3 files changed, 10 insertions(+), 79 deletions(-) diff --git a/crates/containerd-shim-wasmtime/src/executors/container.rs b/crates/containerd-shim-wasmtime/src/executors/container.rs index fc9506ed9..2a78d1beb 100644 --- a/crates/containerd-shim-wasmtime/src/executors/container.rs +++ b/crates/containerd-shim-wasmtime/src/executors/container.rs @@ -1,60 +1,19 @@ use containerd_shim_wasm::sandbox::oci; -use libcontainer::workload::{Executor, ExecutorError, EMPTY}; -use nix::unistd::{dup, dup2}; +use libcontainer::workload::default::DefaultExecutor; +use libcontainer::workload::{Executor, ExecutorError}; -use std::ffi::CString; -use std::io::Read; -use std::{fs::OpenOptions, os::fd::RawFd, path::PathBuf}; - -use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; -use nix::unistd; use oci_spec::runtime::Spec; - -const EXECUTOR_NAME: &str = "default"; +use std::io::Read; +use std::{fs::OpenOptions, path::PathBuf}; #[derive(Default)] pub struct LinuxContainerExecutor { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, + default_executor: DefaultExecutor, } impl Executor for LinuxContainerExecutor { fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> { - log::debug!("executing workload with default handler"); - let args = spec - .process() - .as_ref() - .and_then(|p| p.args().as_ref()) - .unwrap_or(&EMPTY); - - if args.is_empty() { - log::error!("no arguments provided to execute"); - Err(ExecutorError::InvalidArg)?; - } - - redirect_io(self.stdin, self.stdout, self.stderr).map_err(|err| { - log::error!("failed to redirect io: {}", err); - ExecutorError::Other(format!("failed to redirect io: {}", err)) - })?; - - let executable = args[0].as_str(); - let cstring_path = CString::new(executable.as_bytes()).map_err(|err| { - log::error!("failed to convert path {executable:?} to cstring: {}", err,); - ExecutorError::InvalidArg - })?; - let a: Vec = args - .iter() - .map(|s| CString::new(s.as_bytes()).unwrap_or_default()) - .collect(); - unistd::execvp(&cstring_path, &a).map_err(|err| { - log::error!("failed to execvp: {}", err); - ExecutorError::Execution(err.into()) - })?; - - // After execvp is called, the process is replaced with the container - // payload through execvp, so it should never reach here. - unreachable!(); + self.default_executor.exec(spec) } fn can_handle(&self, spec: &Spec) -> bool { @@ -133,22 +92,6 @@ impl Executor for LinuxContainerExecutor { } fn name(&self) -> &'static str { - EXECUTOR_NAME - } -} - -fn redirect_io(stdin: Option, stdout: Option, stderr: Option) -> anyhow::Result<()> { - if let Some(stdin) = stdin { - dup(STDIN_FILENO)?; - dup2(stdin, STDIN_FILENO)?; - } - if let Some(stdout) = stdout { - dup(STDOUT_FILENO)?; - dup2(stdout, STDOUT_FILENO)?; - } - if let Some(stderr) = stderr { - dup(STDERR_FILENO)?; - dup2(stderr, STDERR_FILENO)?; + self.default_executor.name() } - Ok(()) } diff --git a/crates/containerd-shim-wasmtime/src/executors/wasi.rs b/crates/containerd-shim-wasmtime/src/executors/wasi.rs index 1f26a0095..852d72702 100644 --- a/crates/containerd-shim-wasmtime/src/executors/wasi.rs +++ b/crates/containerd-shim-wasmtime/src/executors/wasi.rs @@ -48,16 +48,8 @@ impl Executor for WasmtimeExecutor { let start = args[0].clone(); let mut iterator = start.split('#'); - let mut cmd = iterator.next().unwrap().to_string(); - let stripped = cmd.strip_prefix(std::path::MAIN_SEPARATOR); - if let Some(strpd) = stripped { - cmd = strpd.to_string(); - } - - let mut path = PathBuf::from(cmd); - if path.is_relative() { - path = std::env::current_dir().unwrap().join(path); - } + let cmd = iterator.next().unwrap().to_string(); + let path = PathBuf::from(cmd); // TODO: do we need to validate the wasm binary? // ```rust diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 298cc9423..123ef9941 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -227,11 +227,7 @@ impl Wasi { stdout, stderr, }); - let default_executor = Box::new(LinuxContainerExecutor { - stdin, - stdout, - stderr, - }); + let default_executor = Box::::default(); let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) .with_executor(vec![default_executor, wasmtime_executor])? .with_root_path(self.rootdir.clone())? From 3dee7bf92643738af7890801c8cfb200340d21e3 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Thu, 10 Aug 2023 01:00:21 +0000 Subject: [PATCH 17/18] pass std i/o to default executor Signed-off-by: jiaxiao zhou --- .../containerd-shim-wasmedge/src/executor.rs | 16 ++++++-- .../containerd-shim-wasmedge/src/instance.rs | 6 +-- .../src/executors/container.rs | 38 ++++++++++++++++++- .../src/executors/wasi.rs | 24 ++++++++++-- .../containerd-shim-wasmtime/src/instance.rs | 9 +---- 5 files changed, 73 insertions(+), 20 deletions(-) diff --git a/crates/containerd-shim-wasmedge/src/executor.rs b/crates/containerd-shim-wasmedge/src/executor.rs index 21ab3bf76..b28c927a2 100644 --- a/crates/containerd-shim-wasmedge/src/executor.rs +++ b/crates/containerd-shim-wasmedge/src/executor.rs @@ -15,9 +15,19 @@ use wasmedge_sdk::{ const EXECUTOR_NAME: &str = "wasmedge"; pub struct WasmEdgeExecutor { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, + stdin: Option, + stdout: Option, + stderr: Option, +} + +impl WasmEdgeExecutor { + pub fn new(stdin: Option, stdout: Option, stderr: Option) -> Self { + Self { + stdin, + stdout, + stderr, + } + } } impl Executor for WasmEdgeExecutor { diff --git a/crates/containerd-shim-wasmedge/src/instance.rs b/crates/containerd-shim-wasmedge/src/instance.rs index 52ec2da82..d7f0bcedf 100644 --- a/crates/containerd-shim-wasmedge/src/instance.rs +++ b/crates/containerd-shim-wasmedge/src/instance.rs @@ -236,11 +236,7 @@ impl Wasi { ) -> anyhow::Result { let syscall = create_syscall(); let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) - .with_executor(vec![Box::new(WasmEdgeExecutor { - stdin, - stdout, - stderr, - })])? + .with_executor(vec![Box::new(WasmEdgeExecutor::new(stdin, stdout, stderr))])? .with_root_path(self.rootdir.clone())? .as_init(&self.bundle) .with_systemd(false) diff --git a/crates/containerd-shim-wasmtime/src/executors/container.rs b/crates/containerd-shim-wasmtime/src/executors/container.rs index 2a78d1beb..c1cd3990d 100644 --- a/crates/containerd-shim-wasmtime/src/executors/container.rs +++ b/crates/containerd-shim-wasmtime/src/executors/container.rs @@ -1,18 +1,38 @@ use containerd_shim_wasm::sandbox::oci; use libcontainer::workload::default::DefaultExecutor; use libcontainer::workload::{Executor, ExecutorError}; +use nix::unistd::{dup, dup2}; +use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use oci_spec::runtime::Spec; use std::io::Read; -use std::{fs::OpenOptions, path::PathBuf}; +use std::{fs::OpenOptions, os::fd::RawFd, path::PathBuf}; #[derive(Default)] pub struct LinuxContainerExecutor { + stdin: Option, + stdout: Option, + stderr: Option, default_executor: DefaultExecutor, } +impl LinuxContainerExecutor { + pub fn new(stdin: Option, stdout: Option, stderr: Option) -> Self { + Self { + stdin, + stdout, + stderr, + ..Default::default() + } + } +} + impl Executor for LinuxContainerExecutor { fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> { + redirect_io(self.stdin, self.stdout, self.stderr).map_err(|err| { + log::error!("failed to redirect io: {}", err); + ExecutorError::Other(format!("failed to redirect io: {}", err)) + })?; self.default_executor.exec(spec) } @@ -95,3 +115,19 @@ impl Executor for LinuxContainerExecutor { self.default_executor.name() } } + +fn redirect_io(stdin: Option, stdout: Option, stderr: Option) -> anyhow::Result<()> { + if let Some(stdin) = stdin { + dup(STDIN_FILENO)?; + dup2(stdin, STDIN_FILENO)?; + } + if let Some(stdout) = stdout { + dup(STDOUT_FILENO)?; + dup2(stdout, STDOUT_FILENO)?; + } + if let Some(stderr) = stderr { + dup(STDERR_FILENO)?; + dup2(stderr, STDERR_FILENO)?; + } + Ok(()) +} diff --git a/crates/containerd-shim-wasmtime/src/executors/wasi.rs b/crates/containerd-shim-wasmtime/src/executors/wasi.rs index 852d72702..56792b01b 100644 --- a/crates/containerd-shim-wasmtime/src/executors/wasi.rs +++ b/crates/containerd-shim-wasmtime/src/executors/wasi.rs @@ -15,10 +15,26 @@ use crate::oci_wasmtime::{self, wasi_dir}; const EXECUTOR_NAME: &str = "wasmtime"; pub struct WasmtimeExecutor { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, - pub engine: Engine, + stdin: Option, + stdout: Option, + stderr: Option, + engine: Engine, +} + +impl WasmtimeExecutor { + pub fn new( + stdin: Option, + stdout: Option, + stderr: Option, + engine: Engine, + ) -> Self { + Self { + stdin, + stdout, + stderr, + engine, + } + } } impl Executor for WasmtimeExecutor { diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 123ef9941..e704cb009 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -221,13 +221,8 @@ impl Wasi { let stdout = maybe_open_stdio(stdout).context("could not open stdout")?; let stderr = maybe_open_stdio(stderr).context("could not open stderr")?; - let wasmtime_executor = Box::new(WasmtimeExecutor { - engine, - stdin, - stdout, - stderr, - }); - let default_executor = Box::::default(); + let wasmtime_executor = Box::new(WasmtimeExecutor::new(stdin, stdout, stderr, engine)); + let default_executor = Box::new(LinuxContainerExecutor::new(stdin, stdout, stderr)); let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) .with_executor(vec![default_executor, wasmtime_executor])? .with_root_path(self.rootdir.clone())? From bd4f5b8e8fe8b650c843018af5feac1f4439bcb5 Mon Sep 17 00:00:00 2001 From: jiaxiao zhou Date: Mon, 14 Aug 2023 22:04:30 +0000 Subject: [PATCH 18/18] feat: add container executor to wasmedge shim this commits adds the default executor to the wasmedge shim similar to how wasmtime shim gets it. It made an API change to the containerd-shim-wasm crate by moving default executor logic from wasmtime shim upstream for sharing code. Signed-off-by: jiaxiao zhou --- Makefile | 2 +- crates/containerd-shim-wasm/src/lib.rs | 3 +++ .../container_executor.rs} | 2 +- .../instance.rs} | 2 +- .../src/libcontainer_instance/mod.rs | 5 ++++ .../containerd-shim-wasm/src/sandbox/mod.rs | 6 ----- .../containerd-shim-wasmedge/src/executor.rs | 19 ++++++++++--- .../containerd-shim-wasmedge/src/instance.rs | 9 +++++-- .../src/{executors/wasi.rs => executor.rs} | 0 .../src/executors/mod.rs | 8 ------ .../containerd-shim-wasmtime/src/instance.rs | 4 +-- crates/containerd-shim-wasmtime/src/lib.rs | 2 +- test/k3s/deploy.yaml | 27 ------------------- 13 files changed, 37 insertions(+), 52 deletions(-) rename crates/{containerd-shim-wasmtime/src/executors/container.rs => containerd-shim-wasm/src/libcontainer_instance/container_executor.rs} (99%) rename crates/containerd-shim-wasm/src/{sandbox/libcontainer_instance.rs => libcontainer_instance/instance.rs} (99%) create mode 100644 crates/containerd-shim-wasm/src/libcontainer_instance/mod.rs rename crates/containerd-shim-wasmtime/src/{executors/wasi.rs => executor.rs} (100%) delete mode 100644 crates/containerd-shim-wasmtime/src/executors/mod.rs delete mode 100644 test/k3s/deploy.yaml diff --git a/Makefile b/Makefile index eb78758de..d23229475 100644 --- a/Makefile +++ b/Makefile @@ -111,7 +111,7 @@ test/k3s: target/wasm32-wasi/$(TARGET)/img.tar bin/k3s sudo systemctl restart k3s-runwasi && \ timeout 60 bash -c -- 'while true; do sudo bin/k3s ctr version && break; sleep 1; done' && \ sudo bin/k3s ctr image import --all-platforms target/wasm32-wasi/$(TARGET)/img.tar && \ - sudo bin/k3s kubectl apply -f test/k3s/deploy.yaml + sudo bin/k3s kubectl apply -f test/k8s/deploy.yaml sudo bin/k3s kubectl wait deployment wasi-demo --for condition=Available=True --timeout=90s && \ sudo bin/k3s kubectl get pods -o wide diff --git a/crates/containerd-shim-wasm/src/lib.rs b/crates/containerd-shim-wasm/src/lib.rs index b4a324107..86973ce8c 100644 --- a/crates/containerd-shim-wasm/src/lib.rs +++ b/crates/containerd-shim-wasm/src/lib.rs @@ -6,3 +6,6 @@ pub mod sandbox; pub mod services; + +#[cfg(feature = "libcontainer")] +pub mod libcontainer_instance; diff --git a/crates/containerd-shim-wasmtime/src/executors/container.rs b/crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs similarity index 99% rename from crates/containerd-shim-wasmtime/src/executors/container.rs rename to crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs index c1cd3990d..4da20c797 100644 --- a/crates/containerd-shim-wasmtime/src/executors/container.rs +++ b/crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs @@ -1,4 +1,4 @@ -use containerd_shim_wasm::sandbox::oci; +use crate::sandbox::oci; use libcontainer::workload::default::DefaultExecutor; use libcontainer::workload::{Executor, ExecutorError}; use nix::unistd::{dup, dup2}; diff --git a/crates/containerd-shim-wasm/src/sandbox/libcontainer_instance.rs b/crates/containerd-shim-wasm/src/libcontainer_instance/instance.rs similarity index 99% rename from crates/containerd-shim-wasm/src/sandbox/libcontainer_instance.rs rename to crates/containerd-shim-wasm/src/libcontainer_instance/instance.rs index 28dc99cba..3b2125590 100644 --- a/crates/containerd-shim-wasm/src/sandbox/libcontainer_instance.rs +++ b/crates/containerd-shim-wasm/src/libcontainer_instance/instance.rs @@ -21,7 +21,7 @@ use crate::sandbox::{ use crate::sandbox::InstanceConfig; use std::{path::PathBuf, thread}; -use super::{error::Error, instance::Wait, Instance}; +use crate::sandbox::{error::Error, instance::Wait, Instance}; /// LibcontainerInstance is a trait that gets implemented by a WASI runtime that /// uses youki's libcontainer library as the container runtime. diff --git a/crates/containerd-shim-wasm/src/libcontainer_instance/mod.rs b/crates/containerd-shim-wasm/src/libcontainer_instance/mod.rs new file mode 100644 index 000000000..7d0b07faa --- /dev/null +++ b/crates/containerd-shim-wasm/src/libcontainer_instance/mod.rs @@ -0,0 +1,5 @@ +pub mod container_executor; +pub mod instance; + +pub use container_executor::LinuxContainerExecutor; +pub use instance::LibcontainerInstance; diff --git a/crates/containerd-shim-wasm/src/sandbox/mod.rs b/crates/containerd-shim-wasm/src/sandbox/mod.rs index 5f683d776..0abbac89e 100644 --- a/crates/containerd-shim-wasm/src/sandbox/mod.rs +++ b/crates/containerd-shim-wasm/src/sandbox/mod.rs @@ -2,17 +2,11 @@ use crate::services::sandbox; -// pub mod cgroups; pub mod error; -// pub mod exec; pub mod instance; pub mod instance_utils; -#[cfg(feature = "libcontainer")] -pub mod libcontainer_instance; pub mod manager; pub mod shim; -#[cfg(feature = "libcontainer")] -pub use libcontainer_instance::LibcontainerInstance; pub use error::{Error, Result}; pub use instance::{EngineGetter, Instance, InstanceConfig}; diff --git a/crates/containerd-shim-wasmedge/src/executor.rs b/crates/containerd-shim-wasmedge/src/executor.rs index 5a3792cd6..0660cb11f 100644 --- a/crates/containerd-shim-wasmedge/src/executor.rs +++ b/crates/containerd-shim-wasmedge/src/executor.rs @@ -6,7 +6,7 @@ use oci_spec::runtime::Spec; use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libcontainer::workload::{Executor, ExecutorError}; use log::debug; -use std::os::unix::io::RawFd; +use std::{os::unix::io::RawFd, path::PathBuf}; use wasmedge_sdk::{ config::{CommonConfigOptions, ConfigBuilder, HostRegistrationConfigOptions}, @@ -53,8 +53,21 @@ impl Executor for WasmEdgeExecutor { }; } - fn can_handle(&self, _spec: &Spec) -> bool { - true + fn can_handle(&self, spec: &Spec) -> bool { + // check if the entrypoint of the spec is a wasm binary. + let args = oci::get_args(spec); + if args.is_empty() { + return false; + } + + let start = args[0].clone(); + let mut iterator = start.split('#'); + let cmd = iterator.next().unwrap().to_string(); + let path = PathBuf::from(cmd); + + path.extension() + .map(|ext| ext.to_ascii_lowercase()) + .is_some_and(|ext| ext == "wasm" || ext == "wat") } fn name(&self) -> &'static str { diff --git a/crates/containerd-shim-wasmedge/src/instance.rs b/crates/containerd-shim-wasmedge/src/instance.rs index 9c90090c4..8523fe2b3 100644 --- a/crates/containerd-shim-wasmedge/src/instance.rs +++ b/crates/containerd-shim-wasmedge/src/instance.rs @@ -6,10 +6,12 @@ use std::sync::{Arc, Condvar, Mutex}; use anyhow::Context; use anyhow::Result; +use containerd_shim_wasm::libcontainer_instance::LibcontainerInstance; +use containerd_shim_wasm::libcontainer_instance::LinuxContainerExecutor; use containerd_shim_wasm::sandbox::error::Error; use containerd_shim_wasm::sandbox::instance::ExitCode; use containerd_shim_wasm::sandbox::instance_utils::maybe_open_stdio; -use containerd_shim_wasm::sandbox::{EngineGetter, InstanceConfig, LibcontainerInstance}; +use containerd_shim_wasm::sandbox::{EngineGetter, InstanceConfig}; use libc::{dup2, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use nix::unistd::close; use serde::{Deserialize, Serialize}; @@ -125,8 +127,11 @@ impl LibcontainerInstance for Wasi { let syscall = create_syscall(); let err_others = |err| Error::Others(format!("failed to create container: {}", err)); + let default_executor = Box::new(LinuxContainerExecutor::new(stdin, stdout, stderr)); + let wasmedge_executor = Box::new(WasmEdgeExecutor::new(stdin, stdout, stderr)); + let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) - .with_executor(vec![Box::new(WasmEdgeExecutor::new(stdin, stdout, stderr))]) + .with_executor(vec![default_executor, wasmedge_executor]) .map_err(err_others)? .with_root_path(self.rootdir.clone()) .map_err(err_others)? diff --git a/crates/containerd-shim-wasmtime/src/executors/wasi.rs b/crates/containerd-shim-wasmtime/src/executor.rs similarity index 100% rename from crates/containerd-shim-wasmtime/src/executors/wasi.rs rename to crates/containerd-shim-wasmtime/src/executor.rs diff --git a/crates/containerd-shim-wasmtime/src/executors/mod.rs b/crates/containerd-shim-wasmtime/src/executors/mod.rs deleted file mode 100644 index 54a85e769..000000000 --- a/crates/containerd-shim-wasmtime/src/executors/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! This module contains the `WasmtimeExecutor` and `LinuxContainerExecutor`. -//! - -pub mod container; -pub mod wasi; - -pub use container::LinuxContainerExecutor; -pub use wasi::WasmtimeExecutor; diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 4832fea7b..714f6aa01 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -9,16 +9,16 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, Condvar, Mutex}; use anyhow::Context; +use containerd_shim_wasm::libcontainer_instance::{LibcontainerInstance, LinuxContainerExecutor}; use containerd_shim_wasm::sandbox::error::Error; use containerd_shim_wasm::sandbox::instance::ExitCode; use containerd_shim_wasm::sandbox::instance_utils::maybe_open_stdio; -use containerd_shim_wasm::sandbox::LibcontainerInstance; use containerd_shim_wasm::sandbox::{EngineGetter, InstanceConfig}; use libcontainer::syscall::syscall::create_syscall; use wasmtime::Engine; -use crate::executors::{LinuxContainerExecutor, WasmtimeExecutor}; +use crate::executor::WasmtimeExecutor; static DEFAULT_CONTAINER_ROOT_DIR: &str = "/run/containerd/wasmtime"; pub struct Wasi { diff --git a/crates/containerd-shim-wasmtime/src/lib.rs b/crates/containerd-shim-wasmtime/src/lib.rs index 9cb60e81a..312731e44 100644 --- a/crates/containerd-shim-wasmtime/src/lib.rs +++ b/crates/containerd-shim-wasmtime/src/lib.rs @@ -1,4 +1,4 @@ pub mod error; -pub mod executors; +pub mod executor; pub mod instance; pub mod oci_wasmtime; diff --git a/test/k3s/deploy.yaml b/test/k3s/deploy.yaml deleted file mode 100644 index 41c51fbd3..000000000 --- a/test/k3s/deploy.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: node.k8s.io/v1 -kind: RuntimeClass -metadata: - name: wasm -handler: wasm ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: wasi-demo - labels: - app: wasi-demo -spec: - replicas: 3 - selector: - matchLabels: - app: wasi-demo - template: - metadata: - labels: - app: wasi-demo - spec: - runtimeClassName: wasm - containers: - - name: demo - image: ghcr.io/containerd/runwasi/wasi-demo-app:latest - imagePullPolicy: Never