Merge branch 'main' into v1.4

Signed-off-by: Lann Martin <lann.martin@fermyon.com>
This commit is contained in:
Lann Martin 2023-08-30 15:02:26 -04:00
commit ffb9b07f24
No known key found for this signature in database
GPG Key ID: A30EEE9905396642
66 changed files with 1047 additions and 534 deletions

View File

@ -93,12 +93,11 @@ runs:
using: "composite"
steps:
- name: Install latest Rust stable toolchain
uses: actions-rs/toolchain@v1
shell: bash
if: ${{ inputs.rust == 'true' }}
with:
toolchain: ${{ inputs.rust-version }}
default: true
components: clippy, rustfmt
run: |
rustup toolchain install ${{ inputs.rust-version }} --component clippy --component rustfmt
rustup default ${{ inputs.rust-version }}
- name: "Install Wasm Rust target"
run: rustup target add wasm32-wasi && rustup target add wasm32-unknown-unknown

View File

@ -13,6 +13,12 @@ on:
- "README.md"
- "tests/README.md"
# Serialize workflow runs per ref
# Cancel any outdated, in-flight runs for refs other than 'main'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
CARGO_TERM_COLOR: always
jobs:
@ -150,7 +156,8 @@ jobs:
run: make test-sdk-go
e2e-tests:
runs-on: ubuntu-22.04
# run on a larger runner for more SSD/resource access
runs-on: ubuntu-22.04-4core-spin
needs: build-rust-ubuntu
steps:
- uses: actions/checkout@v3

View File

@ -6,6 +6,12 @@ on:
tags:
- "v*"
# Serialize workflow runs
concurrency: ${{ github.workflow }}-${{ github.ref }}
env:
RUST_VERSION: 1.68
jobs:
build-and-sign:
name: build and sign release assets
@ -29,7 +35,7 @@ jobs:
targetDir: "target/release",
}
- {
os: "ubuntu-latest",
os: "ubuntu-20.04",
arch: "aarch64",
extension: "",
extraArgs: "--features openssl/vendored --target aarch64-unknown-linux-gnu",
@ -85,14 +91,18 @@ jobs:
cosign-release: v2.0.0
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: 1.68
default: true
target: ${{ matrix.config.target }}
shell: bash
run: |
rustup toolchain install ${{ env.RUST_VERSION }}
rustup default ${{ env.RUST_VERSION }}
- name: Install target
if: matrix.config.target != ''
shell: bash
run: rustup target add --toolchain ${{ env.RUST_VERSION }} ${{ matrix.config.target }}
- name: "Install Wasm Rust target"
run: rustup target add wasm32-wasi --toolchain 1.68 && rustup target add wasm32-unknown-unknown --toolchain 1.68
run: rustup target add wasm32-wasi --toolchain ${{ env.RUST_VERSION }} && rustup target add wasm32-unknown-unknown --toolchain ${{ env.RUST_VERSION }}
- name: setup for cross-compiled linux aarch64 build
if: matrix.config.target == 'aarch64-unknown-linux-gnu'
@ -103,10 +113,8 @@ jobs:
echo 'linker = "aarch64-linux-gnu-gcc"' >> ${HOME}/.cargo/config.toml
- name: build release
uses: actions-rs/cargo@v1
with:
command: build
args: "--all-features --release ${{ matrix.config.extraArgs }}"
shell: bash
run: cargo build --all-features --release ${{ matrix.config.extraArgs }}
- name: Sign the binary with GitHub OIDC token
shell: bash

58
Cargo.lock generated
View File

@ -356,9 +356,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.2.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bitvec"
@ -1518,6 +1518,12 @@ dependencies = [
"instant",
]
[[package]]
name = "fastrand"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
[[package]]
name = "fd-lock"
version = "3.0.12"
@ -1688,7 +1694,7 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand",
"fastrand 1.9.0",
"futures-core",
"futures-io",
"memchr",
@ -1753,7 +1759,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd"
dependencies = [
"bitflags 2.2.1",
"bitflags 2.4.0",
"debugid",
"fxhash",
"serde",
@ -1903,7 +1909,7 @@ version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b80172055c5d8017a48ddac5cc7a95421c00211047db0165c97853c4f05194"
dependencies = [
"fastrand",
"fastrand 1.9.0",
"gix-tempfile",
"thiserror",
]
@ -2773,6 +2779,12 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c"
[[package]]
name = "linux-raw-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
name = "liquid"
version = "0.23.1"
@ -3322,9 +3334,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.48"
version = "0.10.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2"
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
@ -3354,11 +3366,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.83"
version = "0.9.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b"
checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
@ -4166,7 +4177,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
dependencies = [
"bitflags 2.2.1",
"bitflags 2.4.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
@ -4259,6 +4270,19 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rustix"
version = "0.38.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
dependencies = [
"bitflags 2.4.0",
"errno 0.3.1",
"libc",
"linux-raw-sys 0.4.5",
"windows-sys 0.48.0",
]
[[package]]
name = "rustls"
version = "0.20.8"
@ -5467,15 +5491,15 @@ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5"
[[package]]
name = "tempfile"
version = "3.5.0"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
dependencies = [
"cfg-if",
"fastrand",
"fastrand 2.0.0",
"redox_syscall 0.3.5",
"rustix 0.37.20",
"windows-sys 0.45.0",
"rustix 0.38.3",
"windows-sys 0.48.0",
]
[[package]]
@ -7013,7 +7037,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "392d16e9e46cc7ca98125bc288dd5e4db469efe8323d3e0dac815ca7f2398522"
dependencies = [
"bitflags 2.2.1",
"bitflags 2.4.0",
"wit-bindgen-rust-macro",
]

View File

@ -61,7 +61,7 @@ spin-plugins = { path = "crates/plugins" }
spin-redis-engine = { path = "crates/redis" }
spin-templates = { path = "crates/templates" }
spin-trigger = { path = "crates/trigger" }
tempfile = "3.3.0"
tempfile = "3.8.0"
tokio = { version = "1.23", features = ["full"] }
toml = "0.6"
tracing = { workspace = true }

View File

@ -115,7 +115,17 @@ fn has_wasm32_wasi_target() -> bool {
fn cargo_build(dir: &str) {
run(
vec!["cargo", "build", "--target", "wasm32-wasi", "--release"],
vec![
"cargo",
"build",
"--target",
"wasm32-wasi",
"--release",
// Ensure that even if `CARGO_TARGET_DIR` is set
// that we're still building into the right dir.
"--target-dir",
"./target",
],
Some(dir),
None,
);
@ -130,8 +140,9 @@ fn run<S: Into<String> + AsRef<std::ffi::OsStr>>(
cmd.stdout(process::Stdio::piped());
cmd.stderr(process::Stdio::piped());
if let Some(dir) = dir {
cmd.current_dir(dir.into());
let dir = dir.map(Into::into);
if let Some(dir) = &dir {
cmd.current_dir(dir);
};
if let Some(env) = env {
@ -141,20 +152,20 @@ fn run<S: Into<String> + AsRef<std::ffi::OsStr>>(
};
cmd.arg("-c");
cmd.arg(
args.into_iter()
.map(Into::into)
.collect::<Vec<String>>()
.join(" "),
);
let c = args
.into_iter()
.map(Into::into)
.collect::<Vec<String>>()
.join(" ");
cmd.arg(&c);
let output = cmd.output().unwrap();
let code = output.status.code().unwrap();
if code != 0 {
println!("{:#?}", std::str::from_utf8(&output.stderr).unwrap());
println!("{:#?}", std::str::from_utf8(&output.stdout).unwrap());
// just fail
assert_eq!(0, code);
let exit = output.status;
if !exit.success() {
println!("{}", std::str::from_utf8(&output.stderr).unwrap());
println!("{}", std::str::from_utf8(&output.stdout).unwrap());
let dir = dir.unwrap_or_else(current_dir);
panic!("while running the build script, the command '{c}' failed to run in '{dir}'")
}
output
@ -167,3 +178,9 @@ fn get_os_process() -> String {
String::from("bash")
}
}
fn current_dir() -> String {
std::env::current_dir()
.map(|d| d.display().to_string())
.unwrap_or_else(|_| String::from("<CURRENT DIR>"))
}

View File

@ -174,7 +174,7 @@ impl<'a, L: MaybeLoader> App<'a, L> {
&'this self,
key: MetadataKey<T>,
) -> Result<Option<T>> {
self.locked.metadata.get_typed(key)
self.locked.get_metadata(key)
}
/// Deserializes typed metadata for this app.
@ -185,7 +185,7 @@ impl<'a, L: MaybeLoader> App<'a, L> {
&'this self,
key: MetadataKey<T>,
) -> Result<T> {
self.locked.metadata.require_typed(key)
self.locked.require_metadata(key)
}
/// Returns an iterator of custom config [`Variable`]s defined for this app.

View File

@ -5,7 +5,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::values::ValuesMap;
use crate::{metadata::MetadataExt, values::ValuesMap};
/// A String-keyed map with deterministic serialization order.
pub type LockedMap<T> = std::collections::BTreeMap<String, T>;
@ -37,6 +37,29 @@ impl LockedApp {
pub fn to_json(&self) -> serde_json::Result<Vec<u8>> {
serde_json::to_vec_pretty(&self)
}
/// Deserializes typed metadata for this app.
///
/// Returns `Ok(None)` if there is no metadata for the given `key` and an
/// `Err` only if there _is_ a value for the `key` but the typed
/// deserialization failed.
pub fn get_metadata<'this, T: Deserialize<'this>>(
&'this self,
key: crate::MetadataKey<T>,
) -> crate::Result<Option<T>> {
self.metadata.get_typed(key)
}
/// Deserializes typed metadata for this app.
///
/// Like [`LockedApp::get_metadata`], but returns an error if there is
/// no metadata for the given `key`.
pub fn require_metadata<'this, T: Deserialize<'this>>(
&'this self,
key: crate::MetadataKey<T>,
) -> crate::Result<T> {
self.metadata.require_typed(key)
}
}
/// A LockedComponent represents a "fully resolved" Spin component.

View File

@ -153,7 +153,9 @@ impl TestCase {
}
// run spin build
let build_output = controller.build_app(&appname).context("building app")?;
let build_output = controller
.build_app(&appname)
.context("failed building app")?;
if bail_on_run_failure {
utils::assert_success(&build_output);
}

View File

@ -1,6 +1,7 @@
pub mod app_info;
pub mod config;
pub mod routes;
pub mod trigger;
pub mod wagi;
pub const WELL_KNOWN_PREFIX: &str = "/.well-known/spin/";

View File

@ -0,0 +1,14 @@
use serde::{Deserialize, Serialize};
use spin_app::MetadataKey;
/// Http trigger metadata key
pub const METADATA_KEY: MetadataKey<Metadata> = MetadataKey::new("trigger");
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Metadata {
// The type of trigger which should always been "http" in this case
pub r#type: String,
// The based url
pub base: String,
}

View File

@ -23,7 +23,7 @@ impl TryFrom<RawVariable> for Variable {
fn try_from(var: RawVariable) -> Result<Self, Self::Error> {
ensure!(
var.required ^ var.default.is_some(),
"variable has both `required` and `default` set"
"variable should either have `required` set to true OR have a non-empty default value"
);
Ok(Variable {
default: var.default,

View File

@ -167,7 +167,13 @@ async fn prepare(
let variables = raw
.variables
.into_iter()
.map(|(key, var)| Ok((key, var.try_into()?)))
.map(|(key, var)| {
Ok((
key.clone(),
var.try_into()
.map_err(|err| anyhow!("variable '{}': {}", key, err))?,
))
})
.collect::<Result<_>>()?;
Ok(Application {

View File

@ -7,14 +7,14 @@ edition = { workspace = true }
[dependencies]
anyhow = "1.0"
bytes = "1.1"
chrono = "0.4"
chrono = { version = "0.4", features = ["serde"] }
dirs = "4.0"
fd-lock = "3.0.12"
flate2 = { version = "1.0.17", features = ["zlib-ng"], default-features = false }
is-terminal = "0.4"
path-absolutize = "3.0.11"
reqwest = { version = "0.11", features = ["json"] }
semver = "1.0"
semver = { version = "1.0", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
spin-common = { path = "../common" }
@ -24,4 +24,4 @@ terminal = { path = "../terminal" }
thiserror = "1"
tokio = { version = "1.23", features = [ "fs", "process", "rt", "macros" ] }
tracing = { workspace = true }
url = "2.2.2"
url = { version = "2.2.2", features = ["serde"] }

View File

@ -170,7 +170,7 @@ impl BadgerEvaluator {
let latest_version = {
let latest_lookup = crate::lookup::PluginLookup::new(&self.plugin_name, None);
let latest_manifest = latest_lookup
.get_manifest_from_repository(store.get_plugins_directory())
.resolve_manifest_exact(store.get_plugins_directory())
.await
.ok();
latest_manifest.and_then(|m| semver::Version::parse(m.version()).ok())

View File

@ -14,6 +14,9 @@ pub enum Error {
#[error("URL parse error {0}")]
UrlParseError(#[from] url::ParseError),
#[error("{0}")]
Other(#[from] anyhow::Error),
}
/// Contains error details for when a plugin resource cannot be found at expected location

View File

@ -30,7 +30,34 @@ impl PluginLookup {
}
}
pub async fn get_manifest_from_repository(
pub async fn resolve_manifest(
&self,
plugins_dir: &Path,
skip_compatibility_check: bool,
spin_version: &str,
) -> PluginLookupResult<PluginManifest> {
let exact = self.resolve_manifest_exact(plugins_dir).await?;
if skip_compatibility_check
|| self.version.is_some()
|| exact.is_compatible_spin_version(spin_version)
{
return Ok(exact);
}
let store = crate::store::PluginStore::new(plugins_dir.to_owned());
// TODO: This is very similar to some logic in the badger module - look for consolidation opportunities.
let manifests = store.catalogue_manifests()?;
let relevant_manifests = manifests.into_iter().filter(|m| m.name() == self.name);
let compatible_manifests = relevant_manifests
.filter(|m| m.has_compatible_package() && m.is_compatible_spin_version(spin_version));
let highest_compatible_manifest =
compatible_manifests.max_by_key(|m| m.try_version().unwrap_or_else(|_| null_version()));
Ok(highest_compatible_manifest.unwrap_or(exact))
}
pub async fn resolve_manifest_exact(
&self,
plugins_dir: &Path,
) -> PluginLookupResult<PluginManifest> {
@ -41,22 +68,48 @@ impl PluginLookup {
.map_err(|e| {
Error::ConnectionFailed(ConnectionFailedError::new(url.to_string(), e.to_string()))
})?;
self.resolve_manifest_exact_from_good_repo(plugins_dir)
}
// This is split from resolve_manifest_exact because it may recurse (once) and that makes
// Rust async sad. So we move the potential recursion to a sync helper.
#[allow(clippy::let_and_return)]
pub fn resolve_manifest_exact_from_good_repo(
&self,
plugins_dir: &Path,
) -> PluginLookupResult<PluginManifest> {
let expected_path = spin_plugins_repo_manifest_path(&self.name, &self.version, plugins_dir);
let file = File::open(&expected_path).map_err(|e| {
Error::NotFound(NotFoundError::new(
let not_found = |e: std::io::Error| {
Err(Error::NotFound(NotFoundError::new(
Some(self.name.clone()),
expected_path.display().to_string(),
e.to_string(),
))
})?;
let manifest: PluginManifest = serde_json::from_reader(file).map_err(|e| {
Error::InvalidManifest(InvalidManifestError::new(
Some(self.name.clone()),
expected_path.display().to_string(),
e.to_string(),
))
})?;
Ok(manifest)
)))
};
let manifest = match File::open(&expected_path) {
Ok(file) => serde_json::from_reader(file).map_err(|e| {
Error::InvalidManifest(InvalidManifestError::new(
Some(self.name.clone()),
expected_path.display().to_string(),
e.to_string(),
))
}),
Err(e) if e.kind() == std::io::ErrorKind::NotFound && self.version.is_some() => {
// If a user has asked for a version by number, and the path doesn't exist,
// it _might_ be because it's the latest version. This checks for that case.
let latest = Self::new(&self.name, None);
match latest.resolve_manifest_exact_from_good_repo(plugins_dir) {
Ok(manifest) if manifest.try_version().ok() == self.version => Ok(manifest),
_ => not_found(e),
}
}
Err(e) => not_found(e),
};
manifest
}
}
@ -64,6 +117,16 @@ pub fn plugins_repo_url() -> Result<Url, url::ParseError> {
Url::parse(SPIN_PLUGINS_REPO)
}
#[cfg(not(test))]
fn accept_as_repo(git_root: &Path) -> bool {
git_root.join(".git").exists()
}
#[cfg(test)]
fn accept_as_repo(git_root: &Path) -> bool {
git_root.join(".git").exists() || git_root.join("_spin_test_dot_git").exists()
}
pub async fn fetch_plugins_repo(
repo_url: &Url,
plugins_dir: &Path,
@ -71,7 +134,7 @@ pub async fn fetch_plugins_repo(
) -> anyhow::Result<()> {
let git_root = plugin_manifests_repo_path(plugins_dir);
let git_source = GitSource::new(repo_url, None, &git_root);
if git_root.join(".git").exists() {
if accept_as_repo(&git_root) {
if update {
git_source.pull().await?;
}
@ -110,3 +173,83 @@ pub fn spin_plugins_repo_manifest_dir(plugins_dir: &Path) -> PathBuf {
.join(PLUGINS_REPO_LOCAL_DIRECTORY)
.join(PLUGINS_REPO_MANIFESTS_DIRECTORY)
}
fn null_version() -> semver::Version {
semver::Version::new(0, 0, 0)
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_NAME: &str = "some-spin-ver-some-not";
const TESTS_STORE_DIR: &str = "tests";
fn tests_store_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(TESTS_STORE_DIR)
}
#[tokio::test]
async fn if_no_version_given_and_latest_is_compatible_then_latest() -> PluginLookupResult<()> {
let lookup = PluginLookup::new(TEST_NAME, None);
let resolved = lookup
.resolve_manifest(&tests_store_dir(), false, "99.0.0")
.await?;
assert_eq!("99.0.1", resolved.version);
Ok(())
}
#[tokio::test]
async fn if_no_version_given_and_latest_is_not_compatible_then_highest_compatible(
) -> PluginLookupResult<()> {
// NOTE: The setup assumes you are NOT running Windows on aarch64, so as to check 98.1.0 is not
// offered. If that assumption fails then this test will fail with actual version being 98.1.0.
// (We use this combination because the OS and architecture enums don't allow for fake operating systems!)
let lookup = PluginLookup::new(TEST_NAME, None);
let resolved = lookup
.resolve_manifest(&tests_store_dir(), false, "98.0.0")
.await?;
assert_eq!("98.0.0", resolved.version);
Ok(())
}
#[tokio::test]
async fn if_version_given_it_gets_used_regardless() -> PluginLookupResult<()> {
let lookup = PluginLookup::new(TEST_NAME, Some(semver::Version::parse("99.0.0").unwrap()));
let resolved = lookup
.resolve_manifest(&tests_store_dir(), false, "98.0.0")
.await?;
assert_eq!("99.0.0", resolved.version);
Ok(())
}
#[tokio::test]
async fn if_latest_version_given_it_gets_used_regardless() -> PluginLookupResult<()> {
let lookup = PluginLookup::new(TEST_NAME, Some(semver::Version::parse("99.0.1").unwrap()));
let resolved = lookup
.resolve_manifest(&tests_store_dir(), false, "98.0.0")
.await?;
assert_eq!("99.0.1", resolved.version);
Ok(())
}
#[tokio::test]
async fn if_no_version_given_but_skip_compat_then_highest() -> PluginLookupResult<()> {
let lookup = PluginLookup::new(TEST_NAME, None);
let resolved = lookup
.resolve_manifest(&tests_store_dir(), true, "98.0.0")
.await?;
assert_eq!("99.0.1", resolved.version);
Ok(())
}
#[tokio::test]
async fn if_non_existent_version_given_then_error() -> PluginLookupResult<()> {
let lookup = PluginLookup::new(TEST_NAME, Some(semver::Version::parse("177.7.7").unwrap()));
lookup
.resolve_manifest(&tests_store_dir(), true, "99.0.0")
.await
.expect_err("Should have errored because plugin v177.7.7 does not exist");
Ok(())
}
}

View File

@ -6,7 +6,7 @@ use crate::{
SPIN_INTERNAL_COMMANDS,
};
use anyhow::{anyhow, bail, Result};
use anyhow::{anyhow, bail, Context, Result};
use path_absolutize::Absolutize;
use serde::Serialize;
use spin_common::sha256;
@ -93,15 +93,26 @@ impl PluginManager {
let target_url = Url::parse(&target)?;
let temp_dir = tempdir()?;
let plugin_tarball_path = match target_url.scheme() {
URL_FILE_SCHEME => target_url
.to_file_path()
.map_err(|_| anyhow!("Invalid file URL: {target_url:?}"))?,
URL_FILE_SCHEME => {
let path = target_url
.to_file_path()
.map_err(|_| anyhow!("Invalid file URL: {target_url:?}"))?;
if path.is_file() {
path
} else {
bail!(
"Package path {} does not exist or is not a file",
path.display()
);
}
}
_ => download_plugin(&plugin_manifest.name(), &temp_dir, &target).await?,
};
verify_checksum(&plugin_tarball_path, &plugin_package.sha256)?;
self.store
.untar_plugin(&plugin_tarball_path, &plugin_manifest.name())?;
.untar_plugin(&plugin_tarball_path, &plugin_manifest.name())
.with_context(|| format!("Failed to untar {}", plugin_tarball_path.display()))?;
// Save manifest to installed plugins directory
self.store.add_manifest(plugin_manifest)?;
@ -173,6 +184,8 @@ impl PluginManager {
pub async fn get_manifest(
&self,
manifest_location: &ManifestLocation,
skip_compatibility_check: bool,
spin_version: &str,
) -> PluginLookupResult<PluginManifest> {
let plugin_manifest = match manifest_location {
ManifestLocation::Remote(url) => {
@ -221,7 +234,11 @@ impl PluginManager {
}
ManifestLocation::PluginsRepository(lookup) => {
lookup
.get_manifest_from_repository(self.store().get_plugins_directory())
.resolve_manifest(
self.store().get_plugins_directory(),
skip_compatibility_check,
spin_version,
)
.await?
}
};
@ -338,7 +355,8 @@ async fn download_plugin(name: &str, temp_dir: &TempDir, target_url: &str) -> Re
}
fn verify_checksum(plugin_file: &Path, expected_sha256: &str) -> Result<()> {
let actual_sha256 = sha256::hex_digest_from_file(plugin_file)?;
let actual_sha256 = sha256::hex_digest_from_file(plugin_file)
.with_context(|| format!("Cannot get digest for {}", plugin_file.display()))?;
if actual_sha256 == expected_sha256 {
log::info!("Package checksum verified successfully");
Ok(())

View File

@ -64,6 +64,10 @@ impl PluginManifest {
Err(_) => false,
}
}
pub fn try_version(&self) -> Result<semver::Version, semver::Error> {
semver::Version::parse(&self.version)
}
}
/// Describes compatibility and location of a plugin source.

View File

@ -0,0 +1 @@
Fake file for preventing the plugin system from pulling from the production repo into this directory during tests

View File

@ -0,0 +1,27 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "99.0.1",
"spinCompatibility": ">=99.0",
"license": "Apache-2.0",
"packages": [
{
"os": "linux",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,27 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "98.0.0",
"spinCompatibility": ">=98.0",
"license": "Apache-2.0",
"packages": [
{
"os": "linux",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,15 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "98.1.0",
"spinCompatibility": ">=98.0",
"license": "Apache-2.0",
"packages": [
{
"os": "windows",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,27 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "99.0.0",
"spinCompatibility": ">=99.0",
"license": "Apache-2.0",
"packages": [
{
"os": "linux",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,21 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "99.0.1",
"spinCompatibility": ">=99.0",
"license": "Apache-2.0",
"packages": [
{
"os": "linux",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,21 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "98.0.0",
"spinCompatibility": ">=98.0",
"license": "Apache-2.0",
"packages": [
{
"os": "linux",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,15 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "98.1.0",
"spinCompatibility": ">=98.0",
"license": "Apache-2.0",
"packages": [
{
"os": "windows",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -0,0 +1,21 @@
{
"name": "some-spin-ver-some-not",
"description": "A plugin where only some versions work with the test harness version of Spin.",
"version": "99.0.0",
"spinCompatibility": ">=99.0",
"license": "Apache-2.0",
"packages": [
{
"os": "linux",
"arch": "amd64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
},
{
"os": "macos",
"arch": "aarch64",
"url": "https://example.com/doesnt-exist",
"sha256": "11111111"
}
]
}

View File

@ -24,3 +24,6 @@ pub use manager::*;
pub use run::{Run, RunOptions};
pub use source::TemplateSource;
pub use template::{Template, TemplateVariantInfo};
#[cfg(test)]
mod test_built_ins;

View File

@ -107,7 +107,11 @@ impl TemplateManager {
/// Creates a `TemplateManager` for the default install location.
pub fn try_default() -> anyhow::Result<Self> {
let store = TemplateStore::try_default()?;
Ok(Self { store })
Ok(Self::new(store))
}
pub(crate) fn new(store: TemplateStore) -> Self {
Self { store }
}
/// Installs templates from the specified source.

View File

@ -0,0 +1,73 @@
#![cfg(test)]
// Module for unit-testing the built-in templates when a full e2e test would be overkill.
// If your test involves invoking the Spin CLI, or builds or runs an application, use
// an e2e test.
use std::{collections::HashMap, path::PathBuf};
use super::*;
struct DiscardingReporter;
impl ProgressReporter for DiscardingReporter {
fn report(&self, _: impl AsRef<str>) {}
}
#[tokio::test]
async fn add_fileserver_does_not_create_dir() -> anyhow::Result<()> {
let built_ins_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
let built_ins_src = TemplateSource::File(built_ins_dir);
let store_dir = tempfile::tempdir()?;
let store = store::TemplateStore::new(store_dir.path());
let manager = TemplateManager::new(store);
manager
.install(
&built_ins_src,
&InstallOptions::default(),
&DiscardingReporter,
)
.await?;
let app_dir = tempfile::tempdir()?;
// Create an app to add the fileserver into
let new_empty_options = RunOptions {
variant: TemplateVariantInfo::NewApplication,
name: "add-fs-dir-test".to_owned(),
output_path: app_dir.path().to_owned(),
values: HashMap::new(),
accept_defaults: true,
};
manager
.get("http-empty")?
.expect("http-empty template should exist")
.run(new_empty_options)
.silent()
.await?;
// Add the fileserver to that app
let manifest_path = app_dir.path().join("spin.toml");
let add_fs_options = RunOptions {
variant: TemplateVariantInfo::AddComponent { manifest_path },
name: "fs".to_owned(),
output_path: app_dir.path().join("fs"),
values: HashMap::new(),
accept_defaults: true,
};
manager
.get("static-fileserver")?
.expect("static-fileserver template should exist")
.run(add_fs_options)
.silent()
.await?;
// Finally!
assert!(
!app_dir.path().join("fs").exists(),
"<app_dir>/fs should not have been created"
);
Ok(())
}

View File

@ -75,7 +75,6 @@ fn color_choice(stream: atty::Stream) -> termcolor::ColorChoice {
#[macro_export]
macro_rules! step {
($step:expr, $($arg:tt)*) => {{
$crate::cprint!($crate::colors::bold_green(), $step);
print!(" ");
println!($($arg)*);

View File

@ -23,8 +23,7 @@ use hyper::{
service::{make_service_fn, service_fn},
Body, Request, Response, Server,
};
use serde::{Deserialize, Serialize};
use spin_app::{AppComponent, MetadataKey};
use spin_app::AppComponent;
use spin_core::Engine;
use spin_http::{
app_info::AppInfo,
@ -44,8 +43,6 @@ pub use tls::TlsConfig;
pub(crate) type RuntimeData = ();
pub(crate) type Store = spin_core::Store<RuntimeData>;
const TRIGGER_METADATA_KEY: MetadataKey<TriggerMetadata> = MetadataKey::new("trigger");
/// The Spin HTTP trigger.
pub struct HttpTrigger {
engine: TriggerAppEngine<Self>,
@ -84,13 +81,6 @@ impl CliArgs {
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct TriggerMetadata {
r#type: String,
base: String,
}
#[async_trait]
impl TriggerExecutor for HttpTrigger {
const TRIGGER_TYPE: &'static str = "http";
@ -99,7 +89,10 @@ impl TriggerExecutor for HttpTrigger {
type RunConfig = CliArgs;
async fn new(engine: TriggerAppEngine<Self>) -> Result<Self> {
let base = engine.app().require_metadata(TRIGGER_METADATA_KEY)?.base;
let base = engine
.app()
.require_metadata(spin_http::trigger::METADATA_KEY)?
.base;
let component_routes = engine
.trigger_configs()
@ -174,17 +167,11 @@ impl TriggerExecutor for HttpTrigger {
if let Some(HttpExecutorType::Wagi(_)) = &config.executor {
let module = component.load_module(engine).await?;
Ok(EitherInstancePre::Module(
engine
.module_instantiate_pre(&module)
.map_err(spin_trigger::decode_preinstantiation_error)?,
engine.module_instantiate_pre(&module)?,
))
} else {
let comp = component.load_component(engine).await?;
Ok(EitherInstancePre::Component(
engine
.instantiate_pre(&comp)
.map_err(spin_trigger::decode_preinstantiation_error)?,
))
Ok(EitherInstancePre::Component(engine.instantiate_pre(&comp)?))
}
}
}
@ -465,6 +452,7 @@ mod tests {
use std::collections::BTreeMap;
use anyhow::Result;
use serde::Deserialize;
use spin_testing::test_socket_addr;
use super::*;

View File

@ -28,7 +28,10 @@ pub const SPIN_WORKING_DIR: &str = "SPIN_WORKING_DIR";
/// A command that runs a TriggerExecutor.
#[derive(Parser, Debug)]
#[clap(next_help_heading = "TRIGGER OPTIONS")]
#[clap(
usage = "spin [COMMAND] [OPTIONS]",
next_help_heading = "TRIGGER OPTIONS"
)]
pub struct TriggerExecutorCommand<Executor: TriggerExecutor>
where
Executor::RunConfig: Args,

View File

@ -56,7 +56,6 @@ pub trait TriggerExecutor: Sized + Send + Sync {
Ok(EitherInstancePre::Component(
engine
.instantiate_pre(&comp)
.map_err(decode_preinstantiation_error)
.with_context(|| format!("Failed to instantiate component '{}'", component.id()))?,
))
}
@ -354,32 +353,3 @@ pub fn parse_file_url(url: &str) -> Result<PathBuf> {
.to_file_path()
.map_err(|_| anyhow!("Invalid file URL path: {url:?}"))
}
pub fn decode_preinstantiation_error(e: anyhow::Error) -> anyhow::Error {
let err_text = e.to_string();
if err_text.contains("unknown import") && err_text.contains("has not been defined") {
// TODO: how to maintain this list?
let sdk_imported_interfaces = &[
"config",
"http",
"key-value",
"mysql",
"postgres",
"redis",
"sqlite",
];
if sdk_imported_interfaces
.iter()
.map(|s| format!("{s}::"))
.any(|s| err_text.contains(&s))
{
return anyhow!(
"{e}. Check that the component uses a SDK or plugin version that matches the Spin runtime."
);
}
}
e
}

View File

@ -80,6 +80,10 @@ To cut a release of Spin, you will need to do the following:
`--certificate-identity` value should match this release, e.g.
`https://github.com/fermyon/spin/.github/workflows/release.yml@refs/tags/v1.1.0`.
1. Create a Pull Request into Fermyon's Hombrew tap repository updating the [Spin
formula](https://github.com/fermyon/homebrew-tap/blob/main/Formula/spin.rb). In the formula,
update the version, point to the latest release artifacts, and set their correct sha256 digests.
The release is now complete!
[release action]: https://github.com/fermyon/spin/actions/workflows/release.yml

View File

@ -0,0 +1,41 @@
# Triage Duty Guide
This guide explains the goals of triage duty and outlines the triage process.
## Goals
1. Identify and properly manage critical issues in a timely manner.
2. Label and prioritize incoming issues.
3. Help move along conversations in issues until they are scoped for the backlog and ready for someone to pick up, closed or resolved.
## Steps
1. Add all new issues to the [`Spin Triage` project board](https://github.com/orgs/fermyon/projects/7/) under the `Triage Needed` column.
To do this:
- Go to the [project board](https://github.com/orgs/fermyon/projects/7/) and select `+ Add Item` at the bottom of the `Triage Needed` column.
- Select `+`
- Select `Add items to project`
- In the search bar, filter issues by `is:issue is:open`
- Select all and push the button `add selected items`
2. For each issue in the `Triage Needed` column, add appropriate labels and move the issue to another column if the issue is ready for a different column (`Investigating / Open for Comment`, `Backlog`, `In progress`) or close.
To do this:
- Determine if the issue is a `bug`, `enhancement`, or `question` and label as such.
- If the issue does not clearly fall into one of these buckets, please ask more questions to determine what labels make sense.
- Please bubble up and help resolve any critical bugs as you come across them.
- If a `question` exposes a need for an improvement in the docs, please open up an issue in the [developer docs repo](https://github.com/fermyon/developer/issues).
- If the issue is being currently investigated, move the issue to the `Investigating / Open for Comment` column and assign the issue an owner (the person who is investigating).
- If the issue is an enhancement and we do not know yet whether we want to address it or it needs more input and discussion, move the issue to the `Investigating / Open for Comment` column. Please also bring this up in the next Spin maintainer or community meeting.
- If the issue is well scoped, we want to resolve it, and it is ready to be picked up, move it to the `Backlog` column.
- If the issue is being actively worked on, please ensure there is an owner and move it to the `In Progress` column.
- If the issue requires no further action or it is a suggestion we don't plan on working on, add an explanation, link to other relevant issues/comments, add the appropriate labels (`duplicate`, `wontfix`) and then close the issue.
3. Visit the [Security Vulnerability tab](https://github.com/fermyon/spin/security/dependabot) to see if there are any outstanding dependabot PRs to review or if any vulnerabilities need to be addressed.
If merging a dependabot PR turns out to be a complicated endevaor and there are reasons for not being able to merge it immediately, leave a comment explaining the situation and where application, link to relevant upstream issues/PRs to watch for progress.
4. Time permitting, review and help move along issues in the `Investigating / Open for Comment` column.

View File

@ -14,12 +14,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -28,15 +22,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.2"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bytes"
version = "1.1.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "fnv"
@ -46,11 +40,10 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
dependencies = [
"matches",
"percent-encoding",
]
@ -71,9 +64,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.6"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [
"bytes",
"fnv",
@ -98,11 +91,10 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.2.3"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
@ -120,9 +112,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.1"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
[[package]]
name = "leb128"
@ -136,29 +128,23 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "percent-encoding"
version = "2.1.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "proc-macro2"
version = "1.0.60"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
dependencies = [
"unicode-ident",
]
@ -176,9 +162,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.28"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
@ -193,6 +179,32 @@ dependencies = [
"smartstring",
]
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.25",
]
[[package]]
name = "smartcow"
version = "0.2.1"
@ -222,12 +234,12 @@ dependencies = [
"http",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "spin-sdk"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"bytes",
@ -247,20 +259,20 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.85"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.18"
version = "2.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
dependencies = [
"proc-macro2",
"quote",
@ -269,38 +281,38 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.85",
"syn 2.0.25",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicase"
@ -319,36 +331,36 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.2"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
@ -399,7 +411,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "392d16e9e46cc7ca98125bc288dd5e4db469efe8323d3e0dac815ca7f2398522"
dependencies = [
"bitflags 2.3.2",
"bitflags 2.3.3",
"wit-bindgen-rust-macro",
]
@ -445,7 +457,7 @@ checksum = "ced38a5e174940c6a41ae587babeadfd2e2c2dc32f3b6488bcdca0e8922cf3f3"
dependencies = [
"anyhow",
"proc-macro2",
"syn 2.0.18",
"syn 2.0.25",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",

View File

@ -87,12 +87,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "async-channel"
version = "1.8.0"
@ -215,17 +209,6 @@ dependencies = [
"getrandom 0.2.8",
]
[[package]]
name = "bigdecimal"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "bincode"
version = "1.3.3"
@ -349,78 +332,12 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "borsh"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa"
dependencies = [
"borsh-derive",
"hashbrown 0.11.2",
]
[[package]]
name = "borsh-derive"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775"
dependencies = [
"borsh-derive-internal",
"borsh-schema-derive-internal",
"proc-macro-crate",
"proc-macro2",
"syn 1.0.107",
]
[[package]]
name = "borsh-derive-internal"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "borsh-schema-derive-internal"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "bytecheck"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f"
dependencies = [
"bytecheck_derive",
"ptr_meta",
]
[[package]]
name = "bytecheck_derive"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -1233,7 +1150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"libz-sys",
"libz-ng-sys",
"miniz_oxide",
]
@ -1267,70 +1184,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "frunk"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89c703bf50009f383a0873845357cc400a95fc535f836feddfe015d7df6e1e0"
dependencies = [
"frunk_core",
"frunk_derives",
"frunk_proc_macros",
]
[[package]]
name = "frunk_core"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a446d01a558301dca28ef43222864a9fa2bd9a2e71370f769d5d5d5ec9f3537"
[[package]]
name = "frunk_derives"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b83164912bb4c97cfe0772913c7af7387ee2e00cb6d4636fb65a35b3d0c8f173"
dependencies = [
"frunk_proc_macro_helpers",
"quote",
"syn 1.0.107",
]
[[package]]
name = "frunk_proc_macro_helpers"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "015425591bbeb0f5b8a75593340f1789af428e9f887a4f1e36c0c471f067ef50"
dependencies = [
"frunk_core",
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "frunk_proc_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea01524f285deab48affffb342b97f186e657b119c3f1821ac531780e0fbfae0"
dependencies = [
"frunk_core",
"frunk_proc_macros_impl",
"proc-macro-hack",
]
[[package]]
name = "frunk_proc_macros_impl"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a802d974cc18ee7fe1a7868fc9ce31086294fd96ba62f8da64ecb44e92a2653"
dependencies = [
"frunk_core",
"frunk_proc_macro_helpers",
"proc-macro-hack",
"quote",
"syn 1.0.107",
]
[[package]]
name = "fs-set-times"
version = "0.19.1"
@ -1560,15 +1413,6 @@ version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -1637,9 +1481,9 @@ dependencies = [
[[package]]
name = "hrana-client-proto"
version = "0.1.2"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26f15d50a607f7f2cb8cb97cad7ae746f861139e8ebc425a8545195a556d6102"
checksum = "f16b4e41e289da3fd60e64f245246a97e78fab7b3788c6d8147b3ae7d9f5e533"
dependencies = [
"anyhow",
"base64 0.21.0",
@ -2044,18 +1888,20 @@ dependencies = [
[[package]]
name = "libsql-client"
version = "0.24.5"
version = "0.31.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8861153820a4228a1261ee92138345f7e08c71e64a75c95217247427172f2ce8"
checksum = "e119ff2e259fe776a1340d2cb40baf4d44c32e5a9fd2755e756fc46802c79c70"
dependencies = [
"anyhow",
"async-trait",
"base64 0.21.0",
"fallible-iterator",
"futures",
"hrana-client-proto",
"num-traits",
"reqwest",
"serde",
"serde_json",
"sqlite3-parser",
"tracing",
"url",
]
@ -2072,14 +1918,13 @@ dependencies = [
]
[[package]]
name = "libz-sys"
version = "1.1.8"
name = "libz-ng-sys"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
checksum = "2468756f34903b582fe7154dc1ffdebd89d0562c4a43b53c621bb0f1b1043ccb"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
"cmake",
"libc",
]
[[package]]
@ -2279,7 +2124,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9006c95034ccf7b903d955f210469119f6c3477fc9c9e7a7845ce38a3e665c2a"
dependencies = [
"base64 0.13.1",
"bigdecimal",
"bindgen",
"bitflags 1.3.2",
"bitvec",
@ -2289,14 +2133,12 @@ dependencies = [
"cmake",
"crc32fast",
"flate2",
"frunk",
"lazy_static",
"lexical",
"num-bigint",
"num-traits",
"rand 0.8.5",
"regex",
"rust_decimal",
"saturating",
"serde",
"serde_json",
@ -2305,8 +2147,6 @@ dependencies = [
"smallvec",
"subprocess",
"thiserror",
"time",
"uuid",
]
[[package]]
@ -2518,7 +2358,7 @@ dependencies = [
[[package]]
name = "outbound-http"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"http",
@ -2532,9 +2372,10 @@ dependencies = [
[[package]]
name = "outbound-mysql"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"flate2",
"mysql_async",
"mysql_common",
"spin-core",
@ -2546,7 +2387,7 @@ dependencies = [
[[package]]
name = "outbound-pg"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"native-tls",
@ -2560,7 +2401,7 @@ dependencies = [
[[package]]
name = "outbound-redis"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"redis",
@ -2678,6 +2519,26 @@ dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
dependencies = [
"phf_shared",
"rand 0.8.5",
]
[[package]]
name = "phf_shared"
version = "0.11.1"
@ -2685,6 +2546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
dependencies = [
"siphasher",
"uncased",
]
[[package]]
@ -2773,15 +2635,6 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -2806,12 +2659,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.54"
@ -2830,26 +2677,6 @@ dependencies = [
"cc",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "pulldown-cmark"
version = "0.8.0"
@ -3050,15 +2877,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "rend"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95"
dependencies = [
"bytecheck",
]
[[package]]
name = "reqwest"
version = "0.11.14"
@ -3119,31 +2937,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "rkyv"
version = "0.7.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15"
dependencies = [
"bytecheck",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
"rkyv_derive",
"seahash",
]
[[package]]
name = "rkyv_derive"
version = "0.7.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "rusqlite"
version = "0.29.0"
@ -3158,24 +2951,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rust_decimal"
version = "1.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe32e8c89834541077a5c5bbe5691aa69324361e27e6aeb3552a737db4a70c8"
dependencies = [
"arrayvec",
"borsh",
"bytecheck",
"byteorder",
"bytes",
"num-traits",
"rand 0.8.5",
"rkyv",
"serde",
"serde_json",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
@ -3344,12 +3119,6 @@ dependencies = [
"untrusted",
]
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "security-framework"
version = "2.8.0"
@ -3617,7 +3386,7 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin-app"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
@ -3630,7 +3399,7 @@ dependencies = [
[[package]]
name = "spin-common"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"dirs",
@ -3652,7 +3421,7 @@ dependencies = [
[[package]]
name = "spin-config"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
@ -3669,7 +3438,7 @@ dependencies = [
[[package]]
name = "spin-core"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
@ -3686,7 +3455,7 @@ dependencies = [
[[package]]
name = "spin-key-value"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"lru 0.9.0",
@ -3739,7 +3508,7 @@ dependencies = [
[[package]]
name = "spin-loader"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
@ -3761,6 +3530,7 @@ dependencies = [
"serde_json",
"shellexpand 3.1.0",
"spin-common",
"spin-config",
"spin-manifest",
"tempfile",
"terminal",
@ -3773,7 +3543,7 @@ dependencies = [
[[package]]
name = "spin-manifest"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"indexmap",
"serde",
@ -3783,9 +3553,10 @@ dependencies = [
[[package]]
name = "spin-sqlite"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
"spin-app",
"spin-core",
"spin-key-value",
@ -3795,9 +3566,10 @@ dependencies = [
[[package]]
name = "spin-sqlite-inproc"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
"once_cell",
"rand 0.8.5",
"rusqlite",
@ -3808,9 +3580,10 @@ dependencies = [
[[package]]
name = "spin-sqlite-libsql"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
"libsql-client",
"spin-sqlite",
"spin-world",
@ -3820,7 +3593,7 @@ dependencies = [
[[package]]
name = "spin-trigger"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"anyhow",
"async-trait",
@ -3861,7 +3634,7 @@ dependencies = [
[[package]]
name = "spin-world"
version = "1.4.0-pre0"
version = "1.5.0-pre0"
dependencies = [
"wasmtime",
]
@ -3872,6 +3645,25 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "sqlite3-parser"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3995a6daa13c113217b6ad22154865fb06f9cb939bef398fd04f4a7aaaf5bd7"
dependencies = [
"bitflags 2.2.1",
"cc",
"fallible-iterator",
"indexmap",
"log",
"memchr",
"phf",
"phf_codegen",
"phf_shared",
"smallvec",
"uncased",
]
[[package]]
name = "sqlparser"
version = "0.34.0"
@ -4326,6 +4118,15 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "uncased"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68"
dependencies = [
"version_check",
]
[[package]]
name = "unicase"
version = "2.6.0"

View File

@ -1,9 +1,9 @@
wit_bindgen::generate!({
world: "spin-timer",
path: "../spin-timer.wit"
path: ".."
});
use fermyon::example::config;
use fermyon::spin::config;
struct MySpinTimer;

View File

@ -0,0 +1,14 @@
package fermyon:spin
interface config {
// Get a configuration value for the current component.
// The config key must match one defined in in the component manifest.
get-config: func(key: string) -> result<string, error>
variant error {
provider(string),
invalid-key(string),
invalid-schema(string),
other(string),
}
}

View File

@ -1,19 +1,6 @@
package fermyon:example
interface config {
// Get a configuration value for the current component.
// The config key must match one defined in in the component manifest.
get-config: func(key: string) -> result<string, error>
variant error {
provider(string),
invalid-key(string),
invalid-schema(string),
other(string),
}
}
world spin-timer {
import config
import fermyon:spin/config
export handle-timer-request: func()
}

View File

@ -10,7 +10,7 @@ use spin_trigger::{
};
wasmtime::component::bindgen!({
path: "spin-timer.wit",
path: ".",
world: "spin-timer",
async: true
});

View File

@ -47,6 +47,10 @@ SDK_VERSION_SOURCE_FILE = sdk_version/sdk-version-go-template.c
SDK_VERSION_DEST_FILES = config/sdk-version-go.c http/sdk-version-go.c \
key_value/sdk-version-go.c redis/sdk-version-go.c
# NOTE: To generate the C bindings you need to install a forked version of wit-bindgen.
#
# cargo install wit-bindgen-cli --git https://github.com/fermyon/wit-bindgen-backport --rev "b89d5079ba5b07b319631a1b191d2139f126c976"
#
.PHONY: generate
generate: $(GENERATED_OUTBOUND_HTTP) $(GENERATED_SPIN_HTTP)
generate: $(GENERATED_OUTBOUND_REDIS) $(GENERATED_SPIN_REDIS)

View File

@ -17,14 +17,18 @@ const spinBinary = "../../target/debug/spin"
func retryGet(t *testing.T, url string) *http.Response {
t.Helper()
const maxTries = 120 // (10min/5sec)
const maxTries = 600 // (10min)
for i := 1; i < maxTries; i++ {
// Catch call to `Fail` in other goroutine
if t.Failed() {
t.FailNow()
}
if res, err := http.Get(url); err != nil {
t.Log(err)
} else {
return res
}
time.Sleep(5 * time.Second)
time.Sleep(1 * time.Second)
}
t.Fatal("Get request timeout: ", url)
return nil
@ -50,6 +54,15 @@ func startSpin(t *testing.T, spinfile string) *testSpin {
t.Fatal(err)
}
go func() {
cmd.Wait()
if ctx.Err() == nil {
t.Log("spin exited before the test finished:", cmd.ProcessState)
t.Log("stderr:\n", stderr.String())
t.Fail()
}
}()
return &testSpin{
cancel: cancel,
url: fmt.Sprintf("http://%s", url),

View File

@ -10,7 +10,7 @@ import (
"unsafe"
)
type Store C.key_value_store_t
type Store uint32
const (
errorKindStoreTableFull = iota

View File

@ -100,7 +100,7 @@ func srem(addr string, key string, values []string) (int64, error) {
return int64(cpayload), toErr(err)
}
type RedisParameterKind C.uint8_t
type RedisParameterKind uint8
const (
RedisParameterKindInt64 = iota
@ -112,7 +112,7 @@ type RedisParameter struct {
Val interface{}
}
type RedisResultKind C.uint8_t
type RedisResultKind uint8
const (
RedisResultKindNil = iota

View File

@ -2,7 +2,6 @@ use anyhow::Error;
use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
use is_terminal::IsTerminal;
use lazy_static::lazy_static;
use spin_cli::build_info::*;
use spin_cli::commands::external::predefined_externals;
use spin_cli::commands::{
build::BuildCommand,
@ -16,6 +15,7 @@ use spin_cli::commands::{
up::UpCommand,
watch::WatchCommand,
};
use spin_cli::{build_info::*, subprocess::ExitStatusError};
use spin_redis_engine::RedisTrigger;
use spin_trigger::cli::help::HelpArgsOnlyTrigger;
use spin_trigger::cli::TriggerExecutorCommand;
@ -24,9 +24,20 @@ use spin_trigger_http::HttpTrigger;
#[tokio::main]
async fn main() {
if let Err(err) = _main().await {
terminal::error!("{err}");
print_error_chain(err);
std::process::exit(1)
let code = match err.downcast_ref::<ExitStatusError>() {
// If we encounter an `ExitStatusError` it means a subprocess has already
// exited unsuccessfully and thus already printed error messages. No need
// to print anything additional.
Some(e) => e.code(),
// Otherwise we print the error chain.
None => {
terminal::error!("{err}");
print_error_chain(err);
1
}
};
std::process::exit(code)
}
}

View File

@ -114,7 +114,13 @@ impl Install {
let manager = PluginManager::try_default()?;
// Downgrades are only allowed via the `upgrade` subcommand
let downgrade = false;
let manifest = manager.get_manifest(&manifest_location).await?;
let manifest = manager
.get_manifest(
&manifest_location,
self.override_compatibility_check,
SPIN_VERSION,
)
.await?;
try_install(
&manifest,
&manager,
@ -250,7 +256,14 @@ impl Upgrade {
.to_string();
let manifest_location =
ManifestLocation::PluginsRepository(PluginLookup::new(&name, None));
let manifest = match manager.get_manifest(&manifest_location).await {
let manifest = match manager
.get_manifest(
&manifest_location,
self.override_compatibility_check,
SPIN_VERSION,
)
.await
{
Err(Error::NotFound(e)) => {
log::info!("Could not upgrade plugin '{name}': {e:?}");
continue;
@ -283,7 +296,13 @@ impl Upgrade {
self.version,
)),
};
let manifest = manager.get_manifest(&manifest_location).await?;
let manifest = manager
.get_manifest(
&manifest_location,
self.override_compatibility_check,
SPIN_VERSION,
)
.await?;
try_install(
&manifest,
&manager,

View File

@ -126,7 +126,7 @@ impl UpCommand {
}
let working_dir_holder = match &self.tmp {
None => WorkingDirectory::Temporary(tempfile::tempdir()?),
None => WorkingDirectory::Temporary(TempDir::with_prefix("spinup-")?),
Some(d) => WorkingDirectory::Given(d.to_owned()),
};
let working_dir = working_dir_holder.path().canonicalize()?;
@ -206,7 +206,7 @@ impl UpCommand {
if status.success() {
Ok(())
} else {
bail!(status);
Err(crate::subprocess::ExitStatusError::new(status).into())
}
}

View File

@ -1,7 +1,8 @@
pub mod build_info;
pub mod commands;
pub(crate) mod opts;
pub mod subprocess;
mod watch_filter;
mod watch_state;
pub use crate::opts::HELP_ARGS_ONLY_TRIGGER_TYPE;
pub use opts::HELP_ARGS_ONLY_TRIGGER_TYPE;

34
src/subprocess.rs Normal file
View File

@ -0,0 +1,34 @@
/// An error representing a subprocess that errored
///
/// This can be used to propogate a subprocesses exit status.
/// When this error is encountered the cli will exit with the status code
/// instead of printing an error,
#[derive(Debug)]
pub struct ExitStatusError {
status: Option<i32>,
}
impl ExitStatusError {
pub(crate) fn new(status: std::process::ExitStatus) -> Self {
Self {
status: status.code(),
}
}
pub fn code(&self) -> i32 {
self.status.unwrap_or(1)
}
}
impl std::error::Error for ExitStatusError {}
impl std::fmt::Display for ExitStatusError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let _ = write!(f, "subprocess exited with status: ");
if let Some(status) = self.status {
writeln!(f, "{}", status)
} else {
writeln!(f, "unknown")
}
}
}

View File

@ -6,8 +6,8 @@ trigger = { type = "http", base = "{{http-base}}" }
version = "0.1.0"
[[component]]
source = { url = "https://github.com/fermyon/spin-fileserver/releases/download/v0.0.2/spin_static_fs.wasm", digest = "sha256:65456bf4e84cf81b62075e761b2b0afaffaef2d0aeda521b245150f76b96421b" }
source = { url = "https://github.com/fermyon/spin-fileserver/releases/download/v0.0.3/spin_static_fs.wasm", digest = "sha256:38bf971900228222f7f6b2ccee5051f399adca58d71692cdfdea98997965fd0d" }
id = "{{ project-name }}"
files = [ { source = "{{ files-path }}", destination = "/" } ]
files = [{ source = "{{ files-path }}", destination = "/" }]
[component.trigger]
route = "{{ http-path | http_wildcard }}"

View File

@ -1,5 +1,5 @@
[[component]]
source = { url = "https://github.com/fermyon/spin-fileserver/releases/download/v0.0.2/spin_static_fs.wasm", digest = "sha256:65456bf4e84cf81b62075e761b2b0afaffaef2d0aeda521b245150f76b96421b" }
source = { url = "https://github.com/fermyon/spin-fileserver/releases/download/v0.0.3/spin_static_fs.wasm", digest = "sha256:38bf971900228222f7f6b2ccee5051f399adca58d71692cdfdea98997965fd0d" }
id = "{{ project-name }}"
files = [ { source = "{{ files-path }}", destination = "/" } ]
[component.trigger]

View File

@ -5,7 +5,7 @@ trigger_type = "http"
tags = ["http", "file", "static", "asset"]
[add_component]
skip_files = ["spin.toml"]
skip_files = ["spin.toml", ".gitignore"]
skip_parameters = ["http-base", "project-description"]
[add_component.snippets]
component = "component.txt"

View File

@ -89,8 +89,13 @@ mod spinup_tests {
}
#[tokio::test]
async fn simple_spin_rust_works() {
testcases::simple_spin_rust_works(CONTROLLER).await
async fn head_rust_sdk_http() {
testcases::head_rust_sdk_http(CONTROLLER).await
}
#[tokio::test]
async fn head_rust_sdk_redis() {
testcases::head_rust_sdk_redis(CONTROLLER).await
}
#[tokio::test]

View File

@ -1,5 +1,5 @@
[package]
name = "simple-spin-rust-test"
name = "head-rust-sdk-http"
version = "0.1.0"
edition = "2021"
@ -17,7 +17,3 @@ http = "0.2"
spin-sdk = { path = "../../../sdk/rust" }
[workspace]
# Metadata about this component.
[package.metadata.component]
name = "spinhelloworld"

View File

@ -1,7 +1,7 @@
spin_version = "1"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
description = "A simple application that returns hello and goodbye."
name = "simple-spin-rust-test"
name = "head-rust-sdk-http"
trigger = {type = "http", base = "/test"}
version = "1.0.0"
@ -10,7 +10,7 @@ object = { default = "teapot" }
[[component]]
id = "hello"
source = "target/wasm32-wasi/release/simple_spin_rust_test.wasm"
source = "target/wasm32-wasi/release/head_rust_sdk_http.wasm"
files = [ { source = "assets", destination = "/" } ]
[component.trigger]
route = "/hello/..."

View File

@ -0,0 +1,2 @@
[build]
target = "wasm32-wasi"

View File

@ -0,0 +1,15 @@
[package]
name = "head-rust-sdk-redis"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = [ "cdylib" ]
[dependencies]
anyhow = "1"
bytes = "1"
http = "0.2"
spin-sdk = { path = "../../../sdk/rust"}
[workspace]

View File

@ -0,0 +1,14 @@
spin_version = "1"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
description = "A simple redis application that exercises the Rust SDK in the current branch"
name = "head-rust-sdk-redis"
trigger = {type = "redis", address = "redis://redis:6379"}
version = "1.0.0"
[[component]]
id = "hello"
source = "target/wasm32-wasi/release/head_rust_sdk_redis.wasm"
[component.trigger]
channel = "my-channel"
[component.build]
command = "cargo build --target wasm32-wasi --release"

View File

@ -0,0 +1,10 @@
use spin_sdk::redis_component;
#[redis_component]
fn on_message(message: bytes::Bytes) -> anyhow::Result<()> {
println!(
"Got message: '{}'",
std::str::from_utf8(&*message).unwrap_or("<MESSAGE NOT UTF8>")
);
Ok(())
}

View File

@ -665,7 +665,8 @@ pub async fn assets_routing_works(controller: &dyn Controller) {
tc.run(controller).await.unwrap()
}
pub async fn simple_spin_rust_works(controller: &dyn Controller) {
/// Test an http app using the current branch's version of the Rust SDK
pub async fn head_rust_sdk_http(controller: &dyn Controller) {
async fn checks(
metadata: AppMetadata,
_: Option<Pin<Box<dyn AsyncBufRead>>>,
@ -719,8 +720,42 @@ pub async fn simple_spin_rust_works(controller: &dyn Controller) {
}
let tc = TestCaseBuilder::default()
.name("simple-spin-rust-test".to_string())
.appname(Some("simple-spin-rust-test".to_string()))
.name("head-rust-sdk-http".to_string())
.appname(Some("head-rust-sdk-http".to_string()))
.assertions(
|metadata: AppMetadata,
stdout_stream: Option<Pin<Box<dyn AsyncBufRead>>>,
stderr_stream: Option<Pin<Box<dyn AsyncBufRead>>>| {
Box::pin(checks(metadata, stdout_stream, stderr_stream))
},
)
.build()
.unwrap();
tc.run(controller).await.unwrap()
}
/// Test a redis app using the current branch's version of the Rust SDK
pub async fn head_rust_sdk_redis(controller: &dyn Controller) {
async fn checks(
_: AppMetadata,
_: Option<Pin<Box<dyn AsyncBufRead>>>,
stderr_stream: Option<Pin<Box<dyn AsyncBufRead>>>,
) -> Result<()> {
wait_for_spin().await;
let stderr = get_output_stream(stderr_stream).await?;
anyhow::ensure!(
stderr.is_empty(),
"expected stderr to be empty, but it was not: {}",
stderr.join("\n")
);
Ok(())
}
let tc = TestCaseBuilder::default()
.name("head-rust-sdk-redis".to_string())
.appname(Some("head-rust-sdk-redis".to_string()))
.trigger_type("redis".to_string())
.assertions(
|metadata: AppMetadata,
stdout_stream: Option<Pin<Box<dyn AsyncBufRead>>>,
@ -877,8 +912,7 @@ pub async fn redis_go_works(controller: &dyn Controller) {
_: Option<Pin<Box<dyn AsyncBufRead>>>,
stderr_stream: Option<Pin<Box<dyn AsyncBufRead>>>,
) -> Result<()> {
//TODO: wait for spin up to be ready dynamically
sleep(Duration::from_secs(10)).await;
wait_for_spin().await;
let output = utils::run(
&[
@ -894,12 +928,13 @@ pub async fn redis_go_works(controller: &dyn Controller) {
)?;
utils::assert_success(&output);
let stderr = utils::get_output_stream(stderr_stream, Duration::from_secs(5)).await?;
let stderr = get_output_stream(stderr_stream).await?;
let expected_logs = vec!["Payload::::", "msg-from-go-channel"];
assert!(expected_logs
.iter()
.all(|item| stderr.contains(&item.to_string())));
.all(|item| stderr.contains(&item.to_string())),
"Expected log lines to contain all of {expected_logs:?} but actual lines were '{stderr:?}'");
Ok(())
}
@ -933,8 +968,7 @@ pub async fn redis_rust_works(controller: &dyn Controller) {
_: Option<Pin<Box<dyn AsyncBufRead>>>,
stderr_stream: Option<Pin<Box<dyn AsyncBufRead>>>,
) -> Result<()> {
//TODO: wait for spin up to be ready dynamically
sleep(Duration::from_secs(20)).await;
wait_for_spin().await;
utils::run(
&[
@ -949,13 +983,14 @@ pub async fn redis_rust_works(controller: &dyn Controller) {
None,
)?;
let stderr = utils::get_output_stream(stderr_stream, Duration::from_secs(5)).await?;
let stderr = get_output_stream(stderr_stream).await?;
let expected_logs = vec!["msg-from-rust-channel"];
assert!(expected_logs
.iter()
.all(|item| stderr.contains(&item.to_string())));
.all(|item| stderr.contains(&item.to_string())),
"Expected log lines to contain all of {expected_logs:?} but actual lines were '{stderr:?}'");
Ok(())
}
@ -1205,3 +1240,14 @@ pub async fn error_messages(controller: &dyn Controller) {
tc.try_run(controller).await.unwrap();
}
async fn get_output_stream(
stream: Option<Pin<Box<dyn AsyncBufRead>>>,
) -> anyhow::Result<Vec<String>> {
utils::get_output_stream(stream, Duration::from_secs(5)).await
}
async fn wait_for_spin() {
//TODO: wait for spin up to be ready dynamically
sleep(Duration::from_secs(10)).await;
}