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 e32e911cd..474639418 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,4 +95,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 e607ed2cc..9b63cca19 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,6 +585,7 @@ dependencies = [ "chrono", "clone3", "command-fds", + "containerd-client", "containerd-shim", "env_logger", "libc", @@ -512,6 +601,7 @@ dependencies = [ "signal-hook", "tempfile", "thiserror", + "tokio", "ttrpc", "ttrpc-codegen", ] @@ -1246,6 +1336,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" @@ -1294,12 +1403,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" @@ -1490,8 +1669,7 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libcgroups" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f6fef16f505466473eeeee906244e03a437beaf41ccd85c39355b4077890c9" +source = "git+https://github.com/jsturtevant/youki/?branch=oci-artifact-skip-verification#d6bf05634336c8c00239f25dd0fd29adef88eebf" dependencies = [ "dbus", "fixedbitset 0.4.2", @@ -1506,8 +1684,7 @@ dependencies = [ [[package]] name = "libcontainer" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac48a05819bd5bd31390bd1874f5a94f711c248677fc908801de4789bdd1fbad" +source = "git+https://github.com/jsturtevant/youki/?branch=oci-artifact-skip-verification#d6bf05634336c8c00239f25dd0fd29adef88eebf" dependencies = [ "bitflags 2.3.3", "caps", @@ -1612,6 +1789,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" @@ -1669,6 +1852,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" @@ -1915,6 +2104,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" @@ -1959,6 +2178,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" @@ -2033,7 +2262,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]] @@ -2047,9 +2286,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", ] @@ -2067,6 +2328,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" @@ -2074,7 +2348,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]] @@ -2362,6 +2645,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" @@ -2530,6 +2819,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" @@ -2576,6 +2875,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" @@ -2701,7 +3006,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]] @@ -2713,6 +3072,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" @@ -2746,6 +3182,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" @@ -2781,9 +3233,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", @@ -2898,6 +3350,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" @@ -3173,7 +3634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4f5b87f1ed383d6c219c04467ab6ae87990d6c2815d5a990138990a7fcbab95" dependencies = [ "anyhow", - "base64", + "base64 0.21.2", "bincode", "directories-next", "file-per-thread-logger", diff --git a/Cargo.toml b/Cargo.toml index 48e2eae8b..3e68a8fba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ thiserror = "1.0" libc = "0.2.147" oci-spec = { version = "0.6.1", features = ["runtime"] } sha256 = "1.2.2" -libcontainer = "0.1" +libcontainer = { git = "https://github.com/jsturtevant/youki/", branch="oci-artifact-skip-verification" } [profile.release] panic = "abort" diff --git a/Makefile b/Makefile index 16f06da3c..6b4448dcf 100644 --- a/Makefile +++ b/Makefile @@ -42,13 +42,29 @@ 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: demo-app +demo-app: target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm + .PHONY: target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm target/wasm32-wasi/$(TARGET)/wasi-demo-app.wasm: rustup target add wasm32-wasi @@ -61,6 +77,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..fad9c8780 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,7 @@ clone3 = "0.2" libc = { workspace = true } caps = "0.5" proc-mounts = "0.3" +tokio = { version = "1.28.2", features = [ "full" ] } [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..7f4342679 --- /dev/null +++ b/crates/containerd-shim-wasm/src/sandbox/containerd.rs @@ -0,0 +1,70 @@ +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 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 + } +} diff --git a/crates/containerd-shim-wasm/src/sandbox/instance.rs b/crates/containerd-shim-wasm/src/sandbox/instance.rs index ce0af303b..27d83b6b9 100644 --- a/crates/containerd-shim-wasm/src/sandbox/instance.rs +++ b/crates/containerd-shim-wasm/src/sandbox/instance.rs @@ -44,11 +44,11 @@ where Self { engine, namespace, + containerd_address, stdin: None, stdout: None, stderr: None, bundle: None, - containerd_address, } } 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..907ade2f5 100644 --- a/crates/containerd-shim-wasm/src/sandbox/oci.rs +++ b/crates/containerd-shim-wasm/src/sandbox/oci.rs @@ -36,6 +36,40 @@ 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.bytecodealliance.wasm.component.layer.v0+wasm") => + { + let containerd_module = annotations + .get("application/vnd.bytecodealliance.wasm.component.layer.v0+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 +194,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-wasmedge/src/executor.rs b/crates/containerd-shim-wasmedge/src/executor.rs index 21ab3bf76..6b70da087 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: String, } 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,78 @@ 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 mut ctrd_client = + containerd::SyncContentClient::connect(self.containerd_address.clone()) + .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 0906d15f0..9a1fd0ecb 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: String, } 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) @@ -417,9 +424,8 @@ mod wasitest { "/containerd/address".into(), )), ); - i.delete().unwrap(); } - + #[test] #[serial] fn test_wasi() -> Result<(), Error> { diff --git a/crates/containerd-shim-wasmtime/src/executor.rs b/crates/containerd-shim-wasmtime/src/executor.rs index df789c98f..a49b5a473 100644 --- a/crates/containerd-shim-wasmtime/src/executor.rs +++ b/crates/containerd-shim-wasmtime/src/executor.rs @@ -1,7 +1,7 @@ use nix::unistd::{dup, dup2}; use std::{fs::OpenOptions, os::fd::RawFd}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; use containerd_shim_wasm::sandbox::oci; use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libcontainer::workload::{Executor, ExecutorError}; @@ -19,6 +19,9 @@ pub struct WasmtimeExecutor { pub stdout: Option, pub stderr: Option, pub engine: Engine, + pub namespace: String, + pub containerd_address: String, + pub module: Option, } impl Executor for WasmtimeExecutor { @@ -80,22 +83,27 @@ 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 &self.module { + Some(m) => m.clone(), + 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 +114,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 0385cb5bf..ef0930e61 100644 --- a/crates/containerd-shim-wasmtime/src/instance.rs +++ b/crates/containerd-shim-wasmtime/src/instance.rs @@ -18,14 +18,14 @@ use anyhow::Context; 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 containerd_shim_wasm::sandbox::{containerd, oci, EngineGetter, Instance, InstanceConfig}; use libc::{dup2, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libc::{SIGINT, SIGKILL}; use libcontainer::syscall::syscall::create_syscall; use log::error; use nix::sys::wait::{Id as WaitID, WaitPidFlag, WaitStatus}; -use wasmtime::Engine; +use wasmtime::{Engine, Module}; use crate::executor::WasmtimeExecutor; use libcontainer::signal::Signal; @@ -46,6 +46,8 @@ pub struct Wasi { bundle: String, rootdir: PathBuf, id: String, + namespace: String, + containerd_address: String, } pub fn reset_stdio() { @@ -110,6 +112,8 @@ impl Instance for Wasi { stderr: cfg.get_stderr().unwrap_or_default(), bundle, rootdir, + namespace: cfg.get_namespace(), + containerd_address: cfg.get_containerd_address(), } } fn start(&self) -> Result { @@ -240,12 +244,16 @@ 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 module = self.load_oci_artifact_module()?; let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) .with_executor(vec![Box::new(WasmtimeExecutor { stdin, stdout, stderr, engine, + namespace: self.namespace.clone(), + containerd_address: self.containerd_address.clone(), + module, })])? .with_root_path(self.rootdir.clone())? .as_init(&self.bundle) @@ -253,6 +261,40 @@ impl Wasi { .build()?; Ok(container) } + + fn load_oci_artifact_module(&self) -> Result, anyhow::Error> { + let spec = load_spec(self.bundle.clone())?; + 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(self.containerd_address.clone())?; + let module = ctrd_client.read_content(oci_module, self.namespace.as_str())?; + let module = Module::from_binary(&self.engine, &module).map_err(|err| { + anyhow::format_err!("could not load module from binary: {}", err) + })?; + Some(module) + } + None => None, + }; + Ok(module) + } +} + +fn load_spec(bundle: String) -> Result { + let mut spec = oci::load(Path::new(&bundle).join("config.json").to_str().unwrap())?; + spec.canonicalize_rootfs(&bundle) + .map_err(|e| Error::Others(format!("error canonicalizing rootfs in spec: {}", e)))?; + Ok(spec) } impl EngineGetter for Wasi { @@ -280,39 +322,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()?; @@ -329,17 +373,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) => { @@ -354,14 +414,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 { @@ -375,6 +435,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) @@ -382,18 +443,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()?; @@ -407,6 +473,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..8afd86b12 100644 --- a/crates/oci-tar-builder/src/bin.rs +++ b/crates/oci-tar-builder/src/bin.rs @@ -22,7 +22,7 @@ pub fn main() { let module_path = PathBuf::from(module_path); builder.add_layer_with_media_type( &module_path, - "application/vnd.w3c.wasm.module.v1+wasm".to_string(), + "application/vnd.bytecodealliance.wasm.component.layer.v0+wasm".to_string(), ); } @@ -36,13 +36,13 @@ pub fn main() { "wasm" => { builder.add_layer_with_media_type( &path, - "application/vnd.wasm.component.v1+wasm".to_string(), + "application/vnd.bytecodealliance.wasm.component.layer.v0+wasm".to_string(), ); } "yml" | "yaml" => { builder.add_layer_with_media_type( &path, - "application/vnd.wasm.component.config.v1+yaml".to_string(), + "application/vnd.bytecodealliance.component.compose.v0+yaml".to_string(), ); } _ => println!( @@ -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 \