Skip to content

Commit

Permalink
Merge pull request #2201 from fermyon/wasi-http-runtime-test
Browse files Browse the repository at this point in the history
  • Loading branch information
rylev authored Dec 22, 2023
2 parents efdc761 + d283347 commit f324fb2
Show file tree
Hide file tree
Showing 22 changed files with 1,320 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions tests/runtime-tests/services/http-echo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from http.server import SimpleHTTPRequestHandler, HTTPServer
import sys


class EchoHandler(SimpleHTTPRequestHandler):
def log_message(self, format, *args):
# Write logs to stdout instead of stderr
log_entry = "[%s] %s\n" % (self.log_date_time_string(), format % args)
sys.stdout.write(log_entry)

def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()

def do_POST(self):
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
self._set_headers()
self.wfile.write(body)


def run(port=8080):
server_address = ('', port)
httpd = HTTPServer(server_address, EchoHandler)
print(f'Starting server on port {port}...')
httpd.serve_forever()


if __name__ == '__main__':
run()
1 change: 1 addition & 0 deletions tests/runtime-tests/tests/wasi-http/services
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http-echo.py
14 changes: 14 additions & 0 deletions tests/runtime-tests/tests/wasi-http/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
spin_manifest_version = 2

[application]
name = "wasi-http"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
version = "0.1.0"

[[trigger.http]]
route = "/"
component = "test"

[component.test]
source = "{{wasi-http-v0.2.0-rc-2023-11-10}}"
allowed_outbound_hosts = ["http://localhost:8080"]
1 change: 1 addition & 0 deletions tests/test-components/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ edition = "2021"

[build-dependencies]
cargo_toml = "0.17.1"
wit-component = "0.19.0"
18 changes: 12 additions & 6 deletions tests/test-components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

Test components for use in runtime testing. Each test component has a README on what it tests. The components are checked into the repository so that users do not necessarily have to build them from source.

## Building
This crate will build all of the components as part of its `build.rs` build script. It then generates code in the lib.rs file which exposes the paths to the built components as constants. For example, for a component named foo-component, a `FOO_COMPONENT` const will be generated with the path to the built component binary.

Each component is generally built like so:
Additionally, a helper function named `path` is generated that maps a package name to binary path for dynamic lookups.

```
cargo b --target=wasm32-wasi
```
## Building

Additionally, to prevent bloat, the components are run through `wasm-tools strip`.
This crate is built like a normal Rust crate: `cargo build`

## Contract

Expand All @@ -19,3 +17,11 @@ Test components have the following contract with the outside world:
* They do not look at the incoming request.
* If nothing errors a 200 with no body will be returned.
* If an error occurs a 500 with a body describing the error will be returned.

## Adapter support

Components can optionally be adapted using a preview 1 to preview 2 adapter (instead of relying on the Spin runtime to do so).

The adapters can be found in the `adapters` directory. They come from the `wasmtime` project and can be downloaded here:

https://github.com/bytecodealliance/wasmtime/releases
Binary file not shown.
52 changes: 40 additions & 12 deletions tests/test-components/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,30 @@ use std::{collections::HashMap, path::PathBuf, process::Command};
fn main() {
println!("cargo:rerun-if-changed=components");
println!("cargo:rerun-if-changed=helper");
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
println!("cargo:rerun-if-changed=adapters");

let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").expect("OUT_DIR env variable not set"));
let packages = std::fs::read_dir("components")
.unwrap()
.expect("could not read components directory")
.filter_map(|e| {
let dir = e.ok()?;
let file_type = dir.file_type().ok()?;
file_type.is_dir().then_some(dir)
})
.map(|e| e.file_name().into_string().unwrap())
.map(|e| {
e.file_name()
.into_string()
.expect("file name is not valid utf8")
})
.collect::<Vec<_>>();

let mut generated_code = String::new();
let mut name_to_path = HashMap::new();
for package in packages {
let crate_path = PathBuf::from("components").join(&package);
let manifest_path = crate_path.join("Cargo.toml");
let manifest = cargo_toml::Manifest::from_path(&manifest_path).unwrap();
let manifest = cargo_toml::Manifest::from_path(&manifest_path)
.expect("failed to read and parse Cargo manifest");

// Build the test component
let mut cargo = Command::new("cargo");
Expand All @@ -30,19 +37,40 @@ fn main() {
.env("RUSTFLAGS", rustflags())
.env("CARGO_TARGET_DIR", &out_dir);
eprintln!("running: {cargo:?}");
let status = cargo.status().unwrap();
let status = cargo.status().expect("`cargo build` failed");
assert!(status.success(), "{status:?}");
eprintln!("{status:?}");
let const_name = to_shouty_snake_case(&package);
let binary_name = manifest.package.unwrap().name.replace('-', "_");
let wasm = out_dir
let package_name = manifest.package.expect("manifest has no package").name;
let binary_name = package_name.replace(['-', '.'], "_");
let wasm_path = out_dir
.join("wasm32-wasi")
.join("debug")
.join(format!("{binary_name}.wasm"));

let adapter_version = package.split('v').last().and_then(|v| match v {
// Only allow this version through
"0.2.0-rc-2023-11-10" => Some(v),
_ => None,
});

if let Some(adapter_version) = adapter_version {
let module_bytes = std::fs::read(&wasm_path).expect("failed to read wasm binary");
let adapter_bytes = std::fs::read(format!("adapters/{adapter_version}.reactor.wasm"))
.expect("failed to read adapter wasm binary");
let new_bytes = wit_component::ComponentEncoder::default()
.validate(true)
.module(&module_bytes)
.expect("failed to set wasm module")
.adapter("wasi_snapshot_preview1", &adapter_bytes)
.expect("failed to apply adapter")
.encode()
.expect("failed to encode component");
std::fs::write(&wasm_path, new_bytes).expect("failed to write new wasm binary");
}

// Generate const with the wasm binary path
generated_code += &format!("pub const {const_name}: &str = {wasm:?};\n");
name_to_path.insert(package, wasm);
generated_code += &format!("pub const {const_name}: &str = {wasm_path:?};\n",);
name_to_path.insert(package, wasm_path);
}

// Generate helper function to map package name to binary path
Expand All @@ -53,11 +81,11 @@ fn main() {
generated_code.push_str(" }\n");
}
generated_code.push_str("None\n}");
std::fs::write(out_dir.join("gen.rs"), generated_code).unwrap();
std::fs::write(out_dir.join("gen.rs"), generated_code).expect("failed to write gen.rs");
}

fn to_shouty_snake_case(package: &str) -> String {
package.to_uppercase().replace('-', "_")
package.to_uppercase().replace(['-', '.'], "_")
}

fn rustflags() -> &'static str {
Expand Down
75 changes: 75 additions & 0 deletions tests/test-components/components/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "wasi-http-rc-2023-11-10"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
url = "2.4.0"
wit-bindgen = "0.16.0"
helper = { path = "../../helper" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Wasi HTTP (v0.2.0-rc-2023-11-10)

Tests the Wasi HTTP outgoing request handler specifically the 0.2.0-rc-2023-11-10 version.

The `wit` directory was copied from https://github.com/bytecodealliance/wasmtime/tree/v15.0.1/crates/wasi/wit and then modified to only include the parts actually used by this component.

## Expectations

This test component expects the following to be true:
* It has access to an HTTP server on localhost:8080 that accepts POST requests and returns the same bytes in the response body as in the request body.
Loading

0 comments on commit f324fb2

Please sign in to comment.