diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 0db980d31..24ef3cb0b 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -1,3 +1,3 @@ #!/bin/bash sudo apt -y update -sudo apt install -y pkg-config libsystemd-dev libdbus-glib-1-dev build-essential libelf-dev libseccomp-dev libclang-dev \ No newline at end of file +sudo apt install -y pkg-config libsystemd-dev libdbus-glib-1-dev build-essential libelf-dev libseccomp-dev libclang-dev protobuf-compiler diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bce2ac81..d892d915d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,4 +93,8 @@ jobs: run: make test/k3s - name: cleanup if: always() - run: make test/k3s/clean + run: | + # run log collection after running tests, so that we can see the logs in case pod doesn't start properly + timeout 5 bash -c -- 'sudo bin/k3s kubectl logs deployments/wasi-demo' + timeout 5 bash -c -- 'sudo bin/k3s kubectl get pods -o wide' + make test/k3s/clean diff --git a/Cargo.lock b/Cargo.lock index eec0a7a1b..86a283efa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,28 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + [[package]] name = "async-trait" version = "0.1.71" @@ -145,6 +167,51 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a1de45611fdb535bfde7b7de4fd54f4fd2b17b1737c0a59b69bf9b92074b8c" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.68" @@ -160,6 +227,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.2" @@ -187,7 +260,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease", + "prettyplease 0.2.10", "proc-macro2", "quote", "regex", @@ -450,6 +523,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "containerd-client" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3bda9d79e851f456e86638109e556ee70bd2fcff661cde0f214f16e094590f" +dependencies = [ + "axum-core", + "prost 0.11.9", + "prost-types 0.11.9", + "tokio", + "tonic", + "tonic-build", + "tower", +] + [[package]] name = "containerd-shim" version = "0.3.0" @@ -497,8 +585,10 @@ dependencies = [ "chrono", "clone3", "command-fds", + "containerd-client", "containerd-shim", "env_logger", + "go-flag", "libc", "log", "nix 0.26.2", @@ -512,6 +602,7 @@ dependencies = [ "signal-hook", "tempfile", "thiserror", + "tokio", "ttrpc", "ttrpc-codegen", ] @@ -1255,6 +1346,25 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1303,12 +1413,82 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1630,6 +1810,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + [[package]] name = "maybe-owned" version = "0.3.4" @@ -1687,6 +1873,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1933,6 +2125,36 @@ dependencies = [ "indexmap 1.9.3", ] +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap 1.9.3", +] + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + [[package]] name = "pin-project-lite" version = "0.2.10" @@ -1977,6 +2199,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + [[package]] name = "prettyplease" version = "0.2.10" @@ -2051,7 +2283,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.8.0", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", ] [[package]] @@ -2065,9 +2307,31 @@ dependencies = [ "itertools", "log", "multimap", - "petgraph", - "prost", - "prost-types", + "petgraph 0.5.1", + "prost 0.8.0", + "prost-types 0.8.0", + "tempfile", + "which", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck 0.4.1", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph 0.6.3", + "prettyplease 0.1.25", + "prost 0.11.9", + "prost-types 0.11.9", + "regex", + "syn 1.0.109", "tempfile", "which", ] @@ -2085,6 +2349,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "prost-types" version = "0.8.0" @@ -2092,7 +2369,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" dependencies = [ "bytes", - "prost", + "prost 0.8.0", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", ] [[package]] @@ -2380,6 +2666,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" @@ -2548,6 +2840,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "sptr" version = "0.3.2" @@ -2594,6 +2896,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-interface" version = "0.25.9" @@ -2720,7 +3028,61 @@ dependencies = [ "autocfg", "backtrace", "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] @@ -2732,6 +3094,83 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.13.1", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.11.9", + "prost-derive 0.11.9", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +dependencies = [ + "prettyplease 0.1.25", + "proc-macro2", + "prost-build 0.11.9", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.37" @@ -2765,6 +3204,22 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "ttrpc" version = "0.8.0" @@ -2800,9 +3255,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3cb5dbf1f0865a34fe3f722290fe776cacb16f50428610b779467b76ddf647" dependencies = [ "derive-new", - "prost", - "prost-build", - "prost-types", + "prost 0.8.0", + "prost-build 0.8.0", + "prost-types 0.8.0", "protobuf 2.28.0", "protobuf-codegen 2.28.0", "tempfile", @@ -2917,6 +3372,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3192,7 +3656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efc78cfe1a758d1336f447a47af6ec05e0df2c03c93440d70faf80e17fbb001e" dependencies = [ "anyhow", - "base64", + "base64 0.21.2", "bincode", "directories-next", "file-per-thread-logger", diff --git a/Makefile b/Makefile index 16f06da3c..6c76f845d 100644 --- a/Makefile +++ b/Makefile @@ -42,13 +42,26 @@ install: $(INSTALL) target/$(TARGET)/containerd-$(runtime)d $(PREFIX)/bin/; \ ) +.PHONY: install/all +install/all: test-image/clean build install test-image load + +.PHONY: instal/oci/all +install/oci/all: test-image/oci/clean build install test-image/oci load/oci + .PHONY: test-image test-image: target/wasm32-wasi/$(TARGET)/img.tar -.PHONY: test-image +.PHONY: test-image/oci +test-image/oci: bin/$(TARGET)/wasi-demo-oci.tar + +.PHONY: test-image/clean test-image/clean: rm -rf target/wasm32-wasi/$(TARGET)/ +.PHONY: test-image/oci/clean +test-image/oci/clean: + rm -rf bin/$(TARGET) + .PHONY: target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm: rustup target add wasm32-wasi @@ -61,6 +74,14 @@ target/wasm32-wasi/$(TARGET)/img.tar: target/wasm32-wasi/$(TARGET)/wasi-demo-app load: target/wasm32-wasi/$(TARGET)/img.tar sudo ctr -n $(CONTAINERD_NAMESPACE) image import --all-platforms $< +load/oci: bin/$(TARGET)/wasi-demo-oci.tar + sudo ../containerd/bin/ctr -n $(CONTAINERD_NAMESPACE) image import --all-platforms $< + +.PHONY: +bin/$(TARGET)/wasi-demo-oci.tar: target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm + mkdir -p ${CURDIR}/bin/$(TARGET)/ + cargo run --bin oci-tar-builder -- --name wasi-demo-oci --repo ghcr.io/containerd/runwasi --tag latest --module ./target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm -o ./bin/$(TARGET) + bin/kind: test/k8s/Dockerfile $(DOCKER_BUILD) --output=bin/ -f test/k8s/Dockerfile --target=kind . diff --git a/README.md b/README.md index 9a42fd4b6..897512abf 100644 --- a/README.md +++ b/README.md @@ -254,3 +254,26 @@ So they'll continue singing it forever just because... To kill the process from demo 2, you can run in other session: `sudo ctr task kill -s SIGKILL testwasm`. The test binary supports commands for different type of functionality, check [crates/wasi-demo-app/src/main.rs](crates/wasi-demo-app/src/main.rs) to try it out. + +## Demo 3 using WASM OCI artifacts + +The previous demos run with an OCI Container image containing the wasm module in the file system. Another option is to provide a cross-platform OCI artifact that that will not have the wasm module or components in the file system of the container that wraps the wasmtime/wasmedge process. This OCI artifact can be run across any platform and provides for de-duplication in the Containerd content store among other benefits. + +To learn more about this approach checkout the [design document](https://docs.google.com/document/d/11shgC3l6gplBjWF1VJCWvN_9do51otscAm0hBDGSSAc/edit). + +> **Note**: This requires containerd components based on https://github.com/containerd/containerd/pull/8699. Both CTR and containerd need to be build with that patch. If you do not have this patch for both `containerd` and `ctr` you will end up with an error message such as `mismatched image rootfs and manifest layers` at the import and run steps + +Build and import the OCI artifact image: + +``` +make test-image/oci +make load/oci +``` + +Run the image with `sudo ctr run --rm --runtime=io.containerd.[ wasmedge | wasmtime ].v1 ghcr.io/containerd/runwasi/wasi-demo-oci:latest testwasmoci` + +``` +sudo ctr run --rm --runtime=io.containerd.wasmtime.v1 ghcr.io/containerd/runwasi/wasi-demo-oci:latest testwasmoci wasi-demo-oci.wasm echo 'hello' +hello +exiting +``` diff --git a/crates/containerd-shim-wasm/Cargo.toml b/crates/containerd-shim-wasm/Cargo.toml index 64b5cb183..fefa677ba 100644 --- a/crates/containerd-shim-wasm/Cargo.toml +++ b/crates/containerd-shim-wasm/Cargo.toml @@ -13,6 +13,7 @@ doctest = false [dependencies] containerd-shim = { workspace = true } +containerd-client = "0.3.0" anyhow = { workspace = true } serde_json = { workspace = true } oci-spec = { workspace = true } @@ -28,6 +29,8 @@ clone3 = "0.2" libc = { workspace = true } caps = "0.5" proc-mounts = "0.3" +tokio = { version = "1.28.2", features = [ "full" ] } +go-flag = "0.1.0" [build-dependencies] ttrpc-codegen = { version = "0.4.2", optional = true } diff --git a/crates/containerd-shim-wasm/src/sandbox/containerd.rs b/crates/containerd-shim-wasm/src/sandbox/containerd.rs new file mode 100644 index 000000000..3abb76348 --- /dev/null +++ b/crates/containerd-shim-wasm/src/sandbox/containerd.rs @@ -0,0 +1,123 @@ +use tokio::runtime::Runtime; + +use crate::sandbox::error::Error as ShimError; +use crate::sandbox::error::Result; +use client::tonic::{self, Streaming}; +use client::{ + services::v1::{content_client::ContentClient, ReadContentRequest, ReadContentResponse}, + tonic::transport::Channel, + with_namespace, +}; +use containerd_client as client; +use std::ffi::OsStr; +use tonic::Request; + +pub struct SyncContentClient { + inner: ContentClient, + rt: Runtime, +} + +// implementation from https://tokio.rs/tokio/topics/bridging +impl SyncContentClient { + // wrapper around connection that will establish a connection and create a content client + pub fn connect(address: String) -> Result { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + + let inner = rt.block_on(async { + client::connect(address) + .await + .map_err(|err| ShimError::Others(err.to_string())) + })?; + + Ok(SyncContentClient { + inner: ContentClient::new(inner), + rt, + }) + } + + // wrapper around read that will read the entire content file + pub fn read_content(&mut self, digest: String, namespace: &str) -> Result> { + let resp: Result> = self.rt.block_on(async { + let req = ReadContentRequest { + digest, + offset: 0, + size: 0, + }; + let req = with_namespace!(req, namespace); + let response: tonic::Response> = self + .inner + .read(req) + .await + .map_err(|err| ShimError::Others(err.to_string()))?; + let mut resp_stream = response.into_inner(); + + let mut data = vec![]; + while let Some(mut next_message) = resp_stream + .message() + .await + .map_err(|err| ShimError::Others(err.to_string()))? + { + data.append(&mut next_message.data); + } + + Ok(data) + }); + + resp + } +} + +// this is from https://github.com/containerd/rust-extensions/blob/main/crates/shim/src/error.rs +// as it not exported +// TODO export upstream and remove this +#[derive(Debug, Default)] +pub struct Flags { + /// Enable debug output in logs. + pub debug: bool, + /// Namespace that owns the shim. + pub namespace: String, + /// Id of the task. + pub id: String, + /// Abstract socket path to serve. + pub socket: String, + /// Path to the bundle if not workdir. + pub bundle: String, + /// GRPC address back to main containerd. + pub address: String, + /// Path to publish binary (used for publishing events). + pub publish_binary: String, + /// Shim action (start / delete). + /// See https://github.com/containerd/containerd/blob/master/runtime/v2/shim/shim.go#L191 + pub action: String, +} + +/// Parses command line arguments passed to the shim. +/// This func replicates https://github.com/containerd/containerd/blob/master/runtime/v2/shim/shim.go#L110 +pub fn parse>(args: &[S]) -> Result { + let mut flags = Flags::default(); + + let args: Vec = go_flag::parse_args(args, |f| { + f.add_flag("debug", &mut flags.debug); + f.add_flag("namespace", &mut flags.namespace); + f.add_flag("id", &mut flags.id); + f.add_flag("socket", &mut flags.socket); + f.add_flag("bundle", &mut flags.bundle); + f.add_flag("address", &mut flags.address); + f.add_flag("publish-binary", &mut flags.publish_binary); + }) + .map_err(|e| ShimError::InvalidArgument(e.to_string()))?; + + if let Some(action) = args.get(0) { + flags.action = action.into(); + } + + if flags.namespace.is_empty() { + return Err(ShimError::InvalidArgument(String::from( + "Shim namespace cannot be empty", + ))); + } + + Ok(flags) +} diff --git a/crates/containerd-shim-wasm/src/sandbox/instance.rs b/crates/containerd-shim-wasm/src/sandbox/instance.rs index 3cc2941df..78edc9590 100644 --- a/crates/containerd-shim-wasm/src/sandbox/instance.rs +++ b/crates/containerd-shim-wasm/src/sandbox/instance.rs @@ -2,12 +2,13 @@ use std::sync::mpsc::Sender; use std::sync::{Arc, Condvar, Mutex}; -use std::thread; +use std::{env, thread}; use libc::{SIGINT, SIGKILL, SIGTERM}; use chrono::{DateTime, Utc}; +use super::containerd; use super::error::Error; type ExitCode = (Mutex)>>, Condvar); @@ -32,6 +33,8 @@ where bundle: Option, /// Namespace for containerd namespace: String, + // /// GRPC address back to main containerd + containerd_address: Option, } impl InstanceConfig @@ -39,9 +42,15 @@ where E: Send + Sync + Clone, { pub fn new(engine: E, namespace: String) -> Self { + let os_args: Vec<_> = env::args_os().collect(); + let containerd_address = match containerd::parse(&os_args[1..]) { + Ok(flags) => Some(flags.address), + _ => None, + }; Self { engine, namespace, + containerd_address, stdin: None, stdout: None, stderr: None, @@ -102,6 +111,11 @@ where pub fn get_namespace(&self) -> String { self.namespace.clone() } + + /// get the containerd address for the instance + pub fn get_containerd_address(&self) -> Option { + self.containerd_address.clone() + } } /// Represents a WASI module(s). diff --git a/crates/containerd-shim-wasm/src/sandbox/mod.rs b/crates/containerd-shim-wasm/src/sandbox/mod.rs index 7494815a7..a89c4184e 100644 --- a/crates/containerd-shim-wasm/src/sandbox/mod.rs +++ b/crates/containerd-shim-wasm/src/sandbox/mod.rs @@ -15,6 +15,7 @@ pub use instance::{EngineGetter, Instance, InstanceConfig}; pub use manager::{Sandbox as SandboxService, Service as ManagerService}; pub use shim::{Cli as ShimCli, Local}; +pub mod containerd; pub mod oci; pub mod testutil; diff --git a/crates/containerd-shim-wasm/src/sandbox/oci.rs b/crates/containerd-shim-wasm/src/sandbox/oci.rs index 909e9afc8..9865322bd 100644 --- a/crates/containerd-shim-wasm/src/sandbox/oci.rs +++ b/crates/containerd-shim-wasm/src/sandbox/oci.rs @@ -36,6 +36,39 @@ pub fn get_args(spec: &Spec) -> &[String] { } } +pub fn get_module(spec: &Spec) -> (Option, String) { + let args = get_args(spec); + + if !args.is_empty() { + 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 method = iterator.next().unwrap_or("_start"); + return (Some(cmd), method.to_string()); + } + + (None, "_start".to_string()) +} + +pub fn get_oci_artifact(spec: &Spec) -> Option { + match spec.annotations().clone() { + Some(annotations) + if annotations.contains_key("application/vnd.w3c.wasm.module.v1+wasm") => + { + let containerd_module = annotations + .get("application/vnd.w3c.wasm.module.v1+wasm") + .unwrap(); + Some(containerd_module.to_string()) + } + _ => None, + } +} + pub fn spec_from_file>(path: P) -> Result { let file = File::open(path)?; let cfg: Spec = json::from_reader(file)?; @@ -160,3 +193,79 @@ pub fn setup_prestart_hooks(hooks: &Option) -> Result< } Ok(()) } + +#[cfg(test)] +mod oci_tests { + use super::*; + use oci_spec::runtime::{ProcessBuilder, RootBuilder, SpecBuilder}; + + #[test] + fn test_get_args() -> Result<()> { + let spec = SpecBuilder::default() + .root(RootBuilder::default().path("rootfs").build()?) + .process( + ProcessBuilder::default() + .cwd("/") + .args(vec!["hello.wat".to_string()]) + .build()?, + ) + .build()?; + + let args = get_args(&spec); + assert_eq!(args.len(), 1); + assert_eq!(args[0], "hello.wat"); + + Ok(()) + } + + #[test] + fn test_get_args_return_empty() -> Result<()> { + let spec = SpecBuilder::default() + .root(RootBuilder::default().path("rootfs").build()?) + .process(ProcessBuilder::default().cwd("/").args(vec![]).build()?) + .build()?; + + let args = get_args(&spec); + assert_eq!(args.len(), 0); + + Ok(()) + } + + #[test] + fn test_get_args_returns_all() -> Result<()> { + let spec = SpecBuilder::default() + .root(RootBuilder::default().path("rootfs").build()?) + .process( + ProcessBuilder::default() + .cwd("/") + .args(vec![ + "hello.wat".to_string(), + "echo".to_string(), + "hello".to_string(), + ]) + .build()?, + ) + .build()?; + + let args = get_args(&spec); + assert_eq!(args.len(), 3); + assert_eq!(args[0], "hello.wat"); + assert_eq!(args[1], "echo"); + assert_eq!(args[2], "hello"); + + Ok(()) + } + + #[test] + fn test_get_module_returns_none_when_not_present() -> Result<()> { + let spec = SpecBuilder::default() + .root(RootBuilder::default().path("rootfs").build()?) + .process(ProcessBuilder::default().cwd("/").args(vec![]).build()?) + .build()?; + + let (module, _) = get_module(&spec); + assert_eq!(module, None); + + Ok(()) + } +} diff --git a/crates/containerd-shim-wasm/src/sandbox/shim.rs b/crates/containerd-shim-wasm/src/sandbox/shim.rs index 7e0a0ec87..04c7c4960 100644 --- a/crates/containerd-shim-wasm/src/sandbox/shim.rs +++ b/crates/containerd-shim-wasm/src/sandbox/shim.rs @@ -1357,6 +1357,7 @@ where type T = Local; fn new(_runtime_id: &str, id: &str, namespace: &str, _config: &mut shim::Config) -> Self { + // Ideally this function passes in either the containerd address or the flags from the cli Cli { engine: I::new_engine().unwrap(), phantom: std::marker::PhantomData, diff --git a/crates/containerd-shim-wasmedge/src/executor.rs b/crates/containerd-shim-wasmedge/src/executor.rs index 21ab3bf76..52767ca00 100644 --- a/crates/containerd-shim-wasmedge/src/executor.rs +++ b/crates/containerd-shim-wasmedge/src/executor.rs @@ -1,12 +1,12 @@ use anyhow::Result; -use containerd_shim_wasm::sandbox::oci; +use containerd_shim_wasm::sandbox::{containerd, oci}; use nix::unistd::{dup, dup2}; 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 wasmedge_sdk::{ config::{CommonConfigOptions, ConfigBuilder, HostRegistrationConfigOptions}, params, VmBuilder, @@ -18,6 +18,8 @@ pub struct WasmEdgeExecutor { pub stdin: Option, pub stdout: Option, pub stderr: Option, + pub namespace: String, + pub containerd_address: Option, } impl Executor for WasmEdgeExecutor { @@ -32,9 +34,9 @@ impl Executor for WasmEdgeExecutor { .prepare(args, spec) .map_err(|err| ExecutorError::Other(format!("failed to prepare function: {}", err)))?; - // TODO: How to get exit code? - // This was relatively straight forward in go, but wasi and wasmtime are totally separate things in rust - match vm.run_func(Some("main"), "_start", params!()) { + let (module_name, method) = oci::get_module(spec); + debug!("running {:?} with method {}", module_name, method); + match vm.run_func(Some("main"), method, params!()) { Ok(_) => std::process::exit(0), Err(_) => std::process::exit(137), }; @@ -50,44 +52,85 @@ impl Executor for WasmEdgeExecutor { } impl WasmEdgeExecutor { - fn prepare(&self, args: &[String], spec: &Spec) -> anyhow::Result { - let mut cmd = args[0].clone(); - if let Some(stripped) = args[0].strip_prefix(std::path::MAIN_SEPARATOR) { - cmd = stripped.to_string(); - } + fn prepare(&self, _args: &[String], spec: &Spec) -> anyhow::Result { let envs = env_to_wasi(spec); + + // create configuration with `wasi` option enabled let config = ConfigBuilder::new(CommonConfigOptions::default()) .with_host_registration_config(HostRegistrationConfigOptions::default().wasi(true)) .build() .map_err(|err| ExecutorError::Execution(err))?; + + // create a vm with the config settings let mut vm = VmBuilder::new() .with_config(config) .build() .map_err(|err| ExecutorError::Execution(err))?; + + // initialize the wasi module with the parsed parameters let wasi_module = vm .wasi_module_mut() .ok_or_else(|| anyhow::Error::msg("Not found wasi module")) .map_err(|err| ExecutorError::Execution(err.into()))?; + + let args = oci::get_args(spec); wasi_module.initialize( Some(args.iter().map(|s| s as &str).collect()), Some(envs.iter().map(|s| s as &str).collect()), None, ); - let vm = vm - .register_module_from_file("main", cmd) - .map_err(|err| ExecutorError::Execution(err))?; + + let (module_name, _) = oci::get_module(spec); + let module_name = match module_name { + Some(m) => m, + None => { + return Err(anyhow::Error::msg( + "no module provided, cannot load module from file within container", + )) + } + }; + + let vm = match oci::get_oci_artifact(spec) { + Some(oci_module) => { + debug!("loading module from annotations"); + let containerd_address = + match &self.containerd_address { + Some(addr) => addr.clone(), + None => return Err(anyhow::Error::msg( + "no containerd address provided, cannot load module from containerd", + )), + }; + + let mut ctrd_client = containerd::SyncContentClient::connect(containerd_address) + .map_err(|err| ExecutorError::Execution(err.into()))?; + let module = ctrd_client + .read_content(oci_module, &self.namespace) + .map_err(|err| ExecutorError::Execution(err.into()))?; + + vm.register_module_from_bytes("main", module) + .map_err(|err| ExecutorError::Execution(err))? + } + None => { + debug!("loading module from file"); + + vm.register_module_from_file("main", module_name) + .map_err(|err| ExecutorError::Execution(err))? + } + }; + if let Some(stdin) = self.stdin { - dup(STDIN_FILENO)?; - dup2(stdin, STDIN_FILENO)?; + let _ = dup(STDIN_FILENO); + let _ = dup2(stdin, STDIN_FILENO); } if let Some(stdout) = self.stdout { - dup(STDOUT_FILENO)?; - dup2(stdout, STDOUT_FILENO)?; + let _ = dup(STDOUT_FILENO); + let _ = dup2(stdout, STDOUT_FILENO); } if let Some(stderr) = self.stderr { - dup(STDERR_FILENO)?; - dup2(stderr, STDERR_FILENO)?; + let _ = dup(STDERR_FILENO); + let _ = dup2(stderr, STDERR_FILENO); } + Ok(vm) } } diff --git a/crates/containerd-shim-wasmedge/src/instance.rs b/crates/containerd-shim-wasmedge/src/instance.rs index 33609343a..bead9eaab 100644 --- a/crates/containerd-shim-wasmedge/src/instance.rs +++ b/crates/containerd-shim-wasmedge/src/instance.rs @@ -57,6 +57,9 @@ pub struct Wasi { bundle: String, rootdir: PathBuf, + + namespace: String, + containerd_address: Option, } pub fn reset_stdio() { @@ -105,12 +108,14 @@ impl Instance for Wasi { let namespace = cfg.get_namespace(); Wasi { id, - rootdir: determine_rootdir(bundle.as_str(), namespace).unwrap(), + rootdir: determine_rootdir(bundle.as_str(), namespace.clone()).unwrap(), exit_code: Arc::new((Mutex::new(None), Condvar::new())), stdin: cfg.get_stdin().unwrap_or_default(), stdout: cfg.get_stdout().unwrap_or_default(), stderr: cfg.get_stderr().unwrap_or_default(), bundle, + namespace, + containerd_address: cfg.get_containerd_address(), } } @@ -240,6 +245,8 @@ impl Wasi { stdin, stdout, stderr, + namespace: self.namespace.clone(), + containerd_address: self.containerd_address.clone(), })])? .with_root_path(self.rootdir.clone())? .as_init(&self.bundle) diff --git a/crates/containerd-shim-wasmtime/src/executor.rs b/crates/containerd-shim-wasmtime/src/executor.rs index cf21ffeb4..c20cb8423 100644 --- a/crates/containerd-shim-wasmtime/src/executor.rs +++ b/crates/containerd-shim-wasmtime/src/executor.rs @@ -1,8 +1,8 @@ use nix::unistd::{dup, dup2}; use std::{fs::OpenOptions, os::fd::RawFd}; -use anyhow::{anyhow, Context, Result}; -use containerd_shim_wasm::sandbox::oci; +use anyhow::{anyhow, Result}; +use containerd_shim_wasm::sandbox::{containerd, oci}; use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libcontainer::workload::{Executor, ExecutorError}; use oci_spec::runtime::Spec; @@ -80,22 +80,36 @@ impl WasmtimeExecutor { log::info!("building wasi context"); let wctx = wasi_builder.build(); - log::info!("wasi context ready"); - let mut iterator = args - .first() - .context("args must have at least one argument.")? - .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 method = iterator.next().unwrap_or("_start"); - let mod_path = cmd; - log::info!("loading module from file"); - let module = Module::from_file(&self.engine, mod_path)?; + let (module_name, method) = oci::get_module(spec); + let module_name = match module_name { + Some(m) => m, + None => { + return Err(anyhow::format_err!( + "no module provided, cannot load module from file within container" + )) + } + }; + + let module = match oci::get_oci_artifact(spec) { + Some(oci_module) => { + log::info!("loading module from annotations"); + let mut ctrd_client = containerd::SyncContentClient::connect( + "/run/containerd/containerd.sock".to_string(), + )?; + let module = ctrd_client.read_content(oci_module, "default")?; + Module::from_binary(&self.engine, &module).map_err(|err| { + anyhow::format_err!("could not load module from file: {}", err) + })? + } + None => { + log::info!("loading module from file"); + let mod_path = oci::get_root(spec).join(module_name); + Module::from_file(&self.engine, mod_path)? + } + }; + let mut linker = Linker::new(&self.engine); wasmtime_wasi::add_to_linker(&mut linker, |s| s)?; @@ -106,7 +120,7 @@ impl WasmtimeExecutor { log::info!("getting start function"); let start_func = instance - .get_func(&mut store, method) + .get_func(&mut store, method.as_str()) .ok_or_else(|| anyhow!("module does not have a WASI start function".to_string()))?; Ok((store, start_func)) } diff --git a/crates/containerd-shim-wasmtime/src/instance.rs b/crates/containerd-shim-wasmtime/src/instance.rs index 5b5be474b..534f535e6 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -283,39 +283,41 @@ mod wasitest { use super::*; // This is taken from https://github.com/bytecodealliance/wasmtime/blob/6a60e8363f50b936e4c4fc958cb9742314ff09f3/docs/WASI-tutorial.md?plain=1#L270-L298 - const WASI_HELLO_WAT: &[u8]= r#"(module - ;; Import the required fd_write WASI function which will write the given io vectors to stdout - ;; The function signature for fd_write is: - ;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written - (import "wasi_unstable" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) - - (memory 1) - (export "memory" (memory 0)) - - ;; Write 'hello world\n' to memory at an offset of 8 bytes - ;; Note the trailing newline which is required for the text to appear - (data (i32.const 8) "hello world\n") - - (func $main (export "_start") - ;; Creating a new io vector within linear memory - (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string - (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string - - (call $fd_write - (i32.const 1) ;; file_descriptor - 1 for stdout - (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 - (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. - (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written + fn hello_world_module(start_fn: Option<&str>) -> Vec { + let start_fn = start_fn.unwrap_or("_start"); + format!(r#"(module + ;; Import the required fd_write WASI function which will write the given io vectors to stdout + ;; The function signature for fd_write is: + ;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written + (import "wasi_unstable" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) + + (memory 1) + (export "memory" (memory 0)) + + ;; Write 'hello world\n' to memory at an offset of 8 bytes + ;; Note the trailing newline which is required for the text to appear + (data (i32.const 8) "hello world\n") + + (func $main (export "{start_fn}") + ;; Creating a new io vector within linear memory + (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string + (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string + + (call $fd_write + (i32.const 1) ;; file_descriptor - 1 for stdout + (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 + (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. + (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written + ) + drop ;; Discard the number of bytes written from the top of the stack ) - drop ;; Discard the number of bytes written from the top of the stack ) - ) - "#.as_bytes(); + "#).as_bytes().to_vec() + } #[test] fn test_delete_after_create() -> Result<()> { - let dir = tempdir()?; - let cfg = prepare_cfg(&dir)?; + let (cfg, _dir) = prepare_cfg(None)?; let i = Wasi::new("".to_string(), Some(&cfg)); i.delete()?; @@ -332,17 +334,33 @@ mod wasitest { // start logging let _ = env_logger::try_init(); - let dir = tempdir()?; - let cfg = prepare_cfg(&dir)?; + let (cfg, dir) = prepare_cfg(None)?; - let wasi = Wasi::new("test".to_string(), Some(&cfg)); + run_module(cfg, dir)?; - wasi.start()?; + Ok(()) + } + #[test] + fn test_wasi_entrypoint() -> Result<(), Error> { + if !has_cap_sys_admin() { + println!("running test with sudo: {}", function!()); + return run_test_with_sudo(function!()); + } + // start logging + let _ = env_logger::try_init(); + + let (cfg, dir) = prepare_cfg(Some("foo"))?; + run_module(cfg, dir)?; + Ok(()) + } + + fn run_module(cfg: InstanceConfig, dir: TempDir) -> Result<(), Error> { + let wasi = Wasi::new("test".to_string(), Some(&cfg)); + wasi.start()?; let (tx, rx) = channel(); let waiter = Wait::new(tx); wasi.wait(&waiter).unwrap(); - let res = match rx.recv_timeout(Duration::from_secs(10)) { Ok(res) => res, Err(e) => { @@ -357,14 +375,14 @@ mod wasitest { let output = read_to_string(dir.path().join("stdout"))?; assert_eq!(output, "hello world\n"); - wasi.delete()?; reset_stdio(); Ok(()) } - fn prepare_cfg(dir: &TempDir) -> Result> { + fn prepare_cfg(start_fn: Option<&str>) -> Result<(InstanceConfig, TempDir)> { + let dir = tempdir()?; create_dir(dir.path().join("rootfs"))?; let opts = Options { @@ -378,6 +396,7 @@ mod wasitest { .open(dir.path().join("options.json"))?; write!(&opts_file, "{}", serde_json::to_string(&opts)?)?; + let module = hello_world_module(start_fn); let wasm_path = dir.path().join("rootfs/hello.wat"); let mut f = OpenOptions::new() .write(true) @@ -385,18 +404,23 @@ mod wasitest { .truncate(true) .mode(0o755) .open(wasm_path)?; - f.write_all(WASI_HELLO_WAT)?; + f.write_all(&module)?; let stdout = File::create(dir.path().join("stdout"))?; let stderr = File::create(dir.path().join("stderr"))?; drop(stdout); drop(stderr); + + let entrypoint = match start_fn { + Some(s) => "./hello.wat#".to_string() + s, + None => "./hello.wat".to_string(), + }; let spec = SpecBuilder::default() .root(RootBuilder::default().path("rootfs").build()?) .process( ProcessBuilder::default() .cwd("/") - .args(vec!["./hello.wat".to_string()]) + .args(vec![entrypoint]) .build()?, ) .build()?; @@ -406,6 +430,6 @@ mod wasitest { .set_bundle(dir.path().to_str().unwrap().to_string()) .set_stdout(dir.path().join("stdout").to_str().unwrap().to_string()) .set_stderr(dir.path().join("stderr").to_str().unwrap().to_string()); - Ok(cfg.to_owned()) + Ok((cfg.to_owned(), dir)) } } diff --git a/crates/oci-tar-builder/src/bin.rs b/crates/oci-tar-builder/src/bin.rs index 98bfd75f9..44d318694 100644 --- a/crates/oci-tar-builder/src/bin.rs +++ b/crates/oci-tar-builder/src/bin.rs @@ -54,7 +54,10 @@ pub fn main() { } } - let config = spec::ConfigBuilder::default().build().unwrap(); + let config = spec::ConfigBuilder::default() + .entrypoint(vec![args.name.clone() + ".wasm"]) + .build() + .unwrap(); let img = spec::ImageConfigurationBuilder::default() .config(config) @@ -70,7 +73,7 @@ pub fn main() { .context("failed to build image configuration") .unwrap(); - builder.add_config(img, args.repo + "/" + &args.name); + builder.add_config(img, args.repo + "/" + &args.name + ":" + &args.tag); let p = out_dir.join(args.name + ".tar"); let f = File::create(p.clone()).unwrap(); @@ -92,6 +95,9 @@ struct Args { #[arg(short, long)] name: String, + #[arg(short, long)] + tag: String, + #[arg(short, long)] repo: String, diff --git a/test/k8s/Dockerfile b/test/k8s/Dockerfile index b0e3c8050..c4e690603 100644 --- a/test/k8s/Dockerfile +++ b/test/k8s/Dockerfile @@ -12,7 +12,7 @@ ENV PATH="/root/.cargo/bin:${PATH}" RUN rustup install stable WORKDIR /shim COPY . . -RUN apt-get update && apt-get install --no-install-recommends -y build-essential git clang wget pkg-config libsystemd-dev libdbus-glib-1-dev build-essential libelf-dev libseccomp-dev libclang-dev +RUN apt-get update && apt-get install --no-install-recommends -y build-essential git clang wget pkg-config libsystemd-dev libdbus-glib-1-dev build-essential libelf-dev libseccomp-dev libclang-dev protobuf-compiler RUN \ --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/shim/target \