diff --git a/Makefile b/Makefile index 3a8ae3971..20a4bdfb8 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,13 @@ E2E_TESTS_DOCKERFILE ?= e2e-tests.Dockerfile MYSQL_IMAGE ?= mysql:8.0.22 REDIS_IMAGE ?= redis:7.0.8-alpine3.17 POSTGRES_IMAGE ?= postgres:14.7-alpine - +REGISTRY_IMAGE ?= registry:2 ## overrides for aarch64 ifneq ($(ARCH),x86_64) MYSQL_IMAGE = arm64v8/mysql:8.0.32 REDIS_IMAGE = arm64v8/redis:6.0-alpine3.17 POSTGRES_IMAGE = arm64v8/postgres:14.7 + REGISTRY_IMAGE = arm64v8/registry:2 E2E_TESTS_DOCKERFILE = e2e-tests-aarch64.Dockerfile endif diff --git a/crates/e2e-testing/src/spin.rs b/crates/e2e-testing/src/spin.rs index 950243844..dcaaffe0d 100644 --- a/crates/e2e-testing/src/spin.rs +++ b/crates/e2e-testing/src/spin.rs @@ -60,6 +60,33 @@ pub fn install_plugins(plugins: Vec<&str>) -> Result { Ok(output) } +pub fn registry_push(appname: &str, registry_app_url: &str) -> Result { + let appdir = appdir(appname); + utils::run( + vec!["spin", "registry", "push", registry_app_url, "--insecure"], + Some(&appdir), + None, + ) +} + +// use docker login until https://github.com/fermyon/spin/issues/1211 +pub fn registry_login(registry_url: &str, username: &str, password: &str) -> Result { + utils::run( + vec![ + "spin", + "registry", + "login", + "-u", + username, + "-p", + password, + registry_url, + ], + None, + None, + ) +} + pub fn build_app(appname: &str) -> Result { let appdir = appdir(appname); utils::run(vec!["spin", "build"], Some(&appdir), None) diff --git a/crates/e2e-testing/src/testcase.rs b/crates/e2e-testing/src/testcase.rs index 1401a5c05..34115a9ee 100644 --- a/crates/e2e-testing/src/testcase.rs +++ b/crates/e2e-testing/src/testcase.rs @@ -66,6 +66,10 @@ pub struct TestCase { /// assertions to run once the app is running pub assertions: ChecksFunc, + + /// registry app url where app is pushed and run from + #[builder(default)] + pub push_to_registry: Option, } impl TestCase { @@ -118,6 +122,11 @@ impl TestCase { // run spin build controller.build_app(&appname).context("building app")?; + //push to registry if url provided + if let Some(registry_app_url) = &self.push_to_registry { + spin::registry_push(&appname, registry_app_url.as_str())?; + } + // run `spin up` (or `spin deploy` for cloud). // `AppInstance` has some basic info about the running app like base url, routes (only for cloud) etc. let deploy_args = self.deploy_args.iter().map(|s| s as &str).collect(); diff --git a/crates/e2e-testing/src/utils.rs b/crates/e2e-testing/src/utils.rs index 20a73c33a..0ff26cc44 100644 --- a/crates/e2e-testing/src/utils.rs +++ b/crates/e2e-testing/src/utils.rs @@ -92,8 +92,7 @@ pub async fn wait_tcp(url: &str, process: &mut tokio::process::Child, target: &s match TcpStream::connect(&url).await { Ok(_) => break, - Err(e) => { - println!("connect {} error {}, retry {}", &url, e, wait_count); + Err(_) => { wait_count += 1; sleep(Duration::from_secs(1)).await; } diff --git a/e2e-tests-aarch64.Dockerfile b/e2e-tests-aarch64.Dockerfile index 6aa43cf52..5cf202ed3 100644 --- a/e2e-tests-aarch64.Dockerfile +++ b/e2e-tests-aarch64.Dockerfile @@ -4,7 +4,7 @@ ARG BUILD_SPIN=false ARG SPIN_VERSION=canary WORKDIR /root -RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis +RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis clang libicu-dev docker.io # nodejs RUN curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - @@ -44,27 +44,33 @@ RUN url="https://static.rust-lang.org/rustup/dist/aarch64-unknown-linux-gnu/rust rustc --version; \ rustup target add wasm32-wasi; +# swift +RUN wget https://github.com/swiftwasm/swift/releases/download/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu20.04_aarch64.tar.gz && \ + tar -xf swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu20.04_aarch64.tar.gz +ENV PATH="$PATH:/root/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/usr/bin" + ## check versions RUN tinygo version; \ go version; \ zig version; \ rustc --version; \ - node --version; + node --version; \ + swift --version; + +## spin +RUN wget https://github.com/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ + tar -xvf spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ + ls -ltr && \ + mv spin /usr/local/bin/spin; WORKDIR /e2e-tests COPY . . -# spin -RUN if [ "${BUILD_SPIN}" != "true" ]; then \ - wget https://github.com/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ - tar -xvf spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ - ls -ltr && \ - mv spin /usr/local/bin/spin; \ - else \ - cargo build --release && \ - cp target/release/spin /usr/local/bin/spin; \ +RUN if [ "${BUILD_SPIN}" == "true" ]; then \ + cargo build --release && \ + cp target/release/spin /usr/local/bin/spin; \ fi RUN spin --version -CMD cargo test spinup_tests --features new-e2e-tests --no-fail-fast -- --nocapture +CMD cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture diff --git a/e2e-tests-docker-compose.yml b/e2e-tests-docker-compose.yml index 7885e15ec..011ead749 100644 --- a/e2e-tests-docker-compose.yml +++ b/e2e-tests-docker-compose.yml @@ -32,11 +32,20 @@ services: - "6379:6379" restart: always + registry: + image: ${REGISTRY_IMAGE:-registry:2} + ports: + - "5000:5000" + restart: always + environment: + - REGISTRY_HTTP_SECRET=secret + e2e-tests: depends_on: - mysql - redis - postgres + - registry image: spin-e2e-tests entrypoint: cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture volumes: @@ -49,4 +58,4 @@ volumes: postgres_data: {} cargo_registry_cache: {} cargo_git_cache: {} - target_cache: {} \ No newline at end of file + target_cache: {} diff --git a/e2e-tests.Dockerfile b/e2e-tests.Dockerfile index 7268f5aa7..a21b558ef 100644 --- a/e2e-tests.Dockerfile +++ b/e2e-tests.Dockerfile @@ -1,7 +1,10 @@ FROM ubuntu:22.04 +ARG BUILD_SPIN=false +ARG SPIN_VERSION=canary + WORKDIR /root -RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis +RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis clang libicu-dev docker.io # nodejs RUN curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - @@ -26,38 +29,48 @@ ENV PATH="$PATH:/root/zig-linux-x86_64-0.10.0" RUN wget https://github.com/grain-lang/grain/releases/download/grain-v0.5.4/grain-linux-x64 && \ mv grain-linux-x64 /usr/local/bin/grain && chmod +x /usr/local/bin/grain -# spin -RUN wget https://github.com/fermyon/spin/releases/download/canary/spin-canary-linux-amd64.tar.gz && \ - tar -xvf spin-canary-linux-amd64.tar.gz && \ - ls -ltr && \ - mv spin /usr/local/bin/spin - # # rust ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ PATH=/usr/local/cargo/bin:$PATH -RUN url="https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init"; \ - wget "$url"; \ - chmod +x rustup-init; \ - ./rustup-init -y --no-modify-path --default-toolchain 1.66 --default-host x86_64-unknown-linux-gnu; \ - rm rustup-init; \ - chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ - rustup --version; \ - cargo --version; \ - rustc --version; \ +RUN url="https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init"; \ + wget "$url"; \ + chmod +x rustup-init; \ + ./rustup-init -y --no-modify-path --default-toolchain 1.66 --default-host x86_64-unknown-linux-gnu; \ + rm rustup-init; \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ + rustup --version; \ + cargo --version; \ + rustc --version; \ rustup target add wasm32-wasi; +# swift +RUN wget https://github.com/swiftwasm/swift/releases/download/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu22.04_x86_64.tar.gz && \ + tar -xf swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu22.04_x86_64.tar.gz +ENV PATH="$PATH:/root/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/usr/bin" + ## check versions -RUN tinygo version -RUN go version -RUN grain --version -RUN spin --version -RUN zig version -RUN rustc --version -RUN node --version +RUN tinygo version; \ + go version; \ + zig version; \ + rustc --version; \ + node --version; \ + swift --version; + +RUN wget https://github.com/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ + tar -xvf spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ + ls -ltr && \ + mv spin /usr/local/bin/spin; WORKDIR /e2e-tests COPY . . -CMD cargo test spinup_tests --features new-e2e-tests --no-fail-fast -- --nocapture +RUN if [ "${BUILD_SPIN}" == "true" ]; then \ + cargo build --release && \ + cp target/release/spin /usr/local/bin/spin; \ + fi + +RUN spin --version + +CMD cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture \ No newline at end of file diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 422231429..e5368cb92 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -46,6 +46,22 @@ mod spinup_tests { testcases::all::http_js_works(CONTROLLER).await } + #[tokio::test] + async fn http_python_works() { + testcases::all::http_python_works(CONTROLLER).await + } + + #[tokio::test] + #[ignore] // https://github.com/fermyon/spin/issues/1210 + async fn http_php_works() { + testcases::all::http_php_works(CONTROLLER).await + } + + #[tokio::test] + async fn http_swift_works() { + testcases::all::http_swift_works(CONTROLLER).await + } + #[tokio::test] async fn assets_routing_works() { testcases::all::assets_routing_works(CONTROLLER).await @@ -85,4 +101,14 @@ mod spinup_tests { async fn redis_rust_works() { testcases::all::redis_rust_works(CONTROLLER).await } + + #[tokio::test] + async fn registry_works() { + testcases::all::registry_works(CONTROLLER).await + } + + #[tokio::test] + async fn longevity_apps_works() { + testcases::all::longevity_apps_works(CONTROLLER).await + } } diff --git a/tests/testcases/longevity-apps-test/README.md b/tests/testcases/longevity-apps-test/README.md new file mode 100644 index 000000000..3319d74a0 --- /dev/null +++ b/tests/testcases/longevity-apps-test/README.md @@ -0,0 +1,12 @@ +## longevity-apps-test + +longevity test is to ensure `wasm` file(s) compiled with a version of `Spin` continues to work with runtime of future version of `Spin`. + +The current wasm files are created using following templates with `Spin v0.9.0` + +- http-go +- http-rust +- http-js +- http-ts + +The `wasm` files are built using `spin build` and copied over here for validation. \ No newline at end of file diff --git a/tests/testcases/longevity-apps-test/longevity-go.wasm b/tests/testcases/longevity-apps-test/longevity-go.wasm new file mode 100755 index 000000000..a6a72b6b5 Binary files /dev/null and b/tests/testcases/longevity-apps-test/longevity-go.wasm differ diff --git a/tests/testcases/longevity-apps-test/longevity-javascript.wasm b/tests/testcases/longevity-apps-test/longevity-javascript.wasm new file mode 100644 index 000000000..230a8309f Binary files /dev/null and b/tests/testcases/longevity-apps-test/longevity-javascript.wasm differ diff --git a/tests/testcases/longevity-apps-test/longevity-rust.wasm b/tests/testcases/longevity-apps-test/longevity-rust.wasm new file mode 100755 index 000000000..00138f084 Binary files /dev/null and b/tests/testcases/longevity-apps-test/longevity-rust.wasm differ diff --git a/tests/testcases/longevity-apps-test/longevity-typescript.wasm b/tests/testcases/longevity-apps-test/longevity-typescript.wasm new file mode 100644 index 000000000..6568a5e1a Binary files /dev/null and b/tests/testcases/longevity-apps-test/longevity-typescript.wasm differ diff --git a/tests/testcases/longevity-apps-test/spin.toml b/tests/testcases/longevity-apps-test/spin.toml new file mode 100644 index 000000000..80690f458 --- /dev/null +++ b/tests/testcases/longevity-apps-test/spin.toml @@ -0,0 +1,30 @@ +spin_version = "1" +authors = ["Fermyon Engineering "] +description = "A longevity test for verifying backward compatible runtime" +name = "longevity-test" +trigger = {type = "http", base = "/"} +version = "1.0.0" + +[[component]] +id = "go" +source = "longevity-go.wasm" +[component.trigger] +route = "/golang/..." + +[[component]] +id = "rust" +source = "longevity-rust.wasm" +[component.trigger] +route = "/rust/..." + +[[component]] +id = "typescript" +source = "longevity-typescript.wasm" +[component.trigger] +route = "/typescript/..." + +[[component]] +id = "javascript" +source = "longevity-javascript.wasm" +[component.trigger] +route = "/javascript/..." \ No newline at end of file diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 07f517635..09c2d3ab4 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -15,6 +15,105 @@ pub mod all { format!("{}{}", base, path) } + pub async fn http_python_works(controller: &dyn Controller) { + async fn checks( + metadata: AppMetadata, + _: Option>, + _: Option>, + ) -> Result<()> { + assert_http_response( + metadata.base.as_str(), + 200, + &[], + Some("Hello from the Python SDK"), + ) + .await + } + + let tc = TestCaseBuilder::default() + .name("http-py-template".to_string()) + .template(Some("http-py".to_string())) + .template_install_args(Some(vec![ + "--git".to_string(), + "https://github.com/fermyon/spin-python-sdk".to_string(), + "--update".to_string(), + ])) + .plugins(Some(vec!["py2wasm".to_string()])) + .assertions( + |metadata: AppMetadata, + stdout_stream: Option>, + stderr_stream: Option>| { + Box::pin(checks(metadata, stdout_stream, stderr_stream)) + }, + ) + .build() + .unwrap(); + + tc.run(controller).await.unwrap() + } + + pub async fn http_php_works(controller: &dyn Controller) { + async fn checks( + metadata: AppMetadata, + _: Option>, + _: Option>, + ) -> Result<()> { + assert_http_response( + metadata.base.as_str(), + 200, + &[], + Some("Hello Fermyon Spin\n"), + ) + .await + } + + let tc = TestCaseBuilder::default() + .name("http-php-template".to_string()) + .template(Some("http-php".to_string())) + .assertions( + |metadata: AppMetadata, + stdout_stream: Option>, + stderr_stream: Option>| { + Box::pin(checks(metadata, stdout_stream, stderr_stream)) + }, + ) + .build() + .unwrap(); + + tc.run(controller).await.unwrap(); + } + + pub async fn http_swift_works(controller: &dyn Controller) { + async fn checks( + metadata: AppMetadata, + _: Option>, + _: Option>, + ) -> Result<()> { + assert_http_response( + metadata.base.as_str(), + 200, + &[], + Some("Hello from WAGI/1!\n"), + ) + .await + } + + let tc = TestCaseBuilder::default() + .name("http-swift-template".to_string()) + .template(Some("http-swift".to_string())) + .assertions( + |metadata: AppMetadata, + stdout_stream: Option>, + stderr_stream: Option>| { + Box::pin(checks(metadata, stdout_stream, stderr_stream)) + }, + ) + .build() + .unwrap(); + + tc.run(controller).await.unwrap(); + } + pub async fn http_go_works(controller: &dyn Controller) { async fn checks( metadata: AppMetadata, @@ -635,4 +734,98 @@ pub mod all { tc.run(controller).await.unwrap() } + + pub async fn registry_works(controller: &dyn Controller) { + async fn checks( + metadata: AppMetadata, + _: Option>, + _: Option>, + ) -> Result<()> { + assert_http_response(metadata.base.as_str(), 200, &[], Some("Hello Fermyon!\n")).await + } + + let registry = "registry:5000"; + let registry_app_url = format!( + "{}/{}/{}:{}", + registry, "spin-e2e-tests", "registry_works", "v1" + ); + let tc = TestCaseBuilder::default() + .name("http-go".to_string()) + .template(Some("http-go".to_string())) + .appname(Some("http-go-registry-generated".to_string())) + .push_to_registry(Some(registry_app_url.clone())) + .deploy_args(vec![ + "--from-registry".to_string(), + registry_app_url.clone(), + "--insecure".to_string(), + ]) + .assertions( + |metadata: AppMetadata, + stdout_stream: Option>, + stderr_stream: Option>| { + Box::pin(checks(metadata, stdout_stream, stderr_stream)) + }, + ) + .build() + .unwrap(); + + tc.run(controller).await.unwrap() + } + + pub async fn longevity_apps_works(controller: &dyn Controller) { + async fn checks( + metadata: AppMetadata, + _: Option>, + _: Option>, + ) -> Result<()> { + assert_http_response( + get_url(metadata.base.as_str(), "/golang").as_str(), + 200, + &[], + Some("Hello Fermyon!\n"), + ) + .await?; + + assert_http_response( + get_url(metadata.base.as_str(), "/rust").as_str(), + 200, + &[], + Some("Hello, Fermyon"), + ) + .await?; + + assert_http_response( + get_url(metadata.base.as_str(), "/javascript").as_str(), + 200, + &[], + Some("Hello from JS-SDK"), + ) + .await?; + + assert_http_response( + get_url(metadata.base.as_str(), "/typescript").as_str(), + 200, + &[], + Some("Hello from TS-SDK"), + ) + .await?; + + Ok(()) + } + + let tc = TestCaseBuilder::default() + .name("longevity-apps-test".to_string()) + .appname(Some("longevity-apps-test".to_string())) + .assertions( + |metadata: AppMetadata, + stdout_stream: Option>, + stderr_stream: Option>| { + Box::pin(checks(metadata, stdout_stream, stderr_stream)) + }, + ) + .build() + .unwrap(); + + tc.run(controller).await.unwrap() + } }