Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ members = [
"components/extract-wit",
"components/filesystem-loader",
"components/http-loader",
"components/oci-loader",
"components/path-loader-router",
"components/test-loader-harness",
"components/wac-loader",
]

Expand Down
17 changes: 2 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,6 @@ endef

$(foreach name,$(TOOLS),$(eval $(call INSTALL_TOOL,$(name))))

# the target a cargo component is built for, the package's `default-target`, e.g. `wasm32-wasip3`.
# wasm32-unknown-unknown builds a core module that is wrapped as a component, a wasi target builds
# a component.
cargo_target = $(or $(shell sed -n 's/^default-target = "\(.*\)"$$/\1/p' components/$(1)/Cargo.toml),wasm32-unknown-unknown)

.PHONY: components
components: ${COMPONENTS_DIR}/interface.wasm $(foreach component,$(COMPONENTS),${COMPONENTS_DIR}/$(component)/$(component).wasm ${COMPONENTS_DIR}/$(component)/$(component).debug.wasm)

Expand Down Expand Up @@ -118,20 +113,12 @@ ${COMPONENTS_DIR}/$1/$1.debug.wasm: components/$1/$1.wkg ${COMPONENTS_DIR}/$1/RE
else ifneq ($(wildcard components/$1/Cargo.toml),)

${COMPONENTS_DIR}/$1/$1.wasm: Cargo.toml Cargo.lock components/wit/deps $(shell find components/$1 -type f) $(shell find crates -type f 2> /dev/null) ${COMPONENTS_DIR}/$1/README.md | $(call tool,wasm-tools)
cargo build -p $1 --target $(call cargo_target,$1) --release
ifeq ($(call cargo_target,$1),wasm32-unknown-unknown)
cargo build -p $1 --target wasm32-unknown-unknown --release
wasm-tools component new target/wasm32-unknown-unknown/release/$(subst -,_,$1).wasm -o ${COMPONENTS_DIR}/$1/$1.wasm
else
cp target/$(call cargo_target,$1)/release/$(subst -,_,$1).wasm ${COMPONENTS_DIR}/$1/$1.wasm
endif

${COMPONENTS_DIR}/$1/$1.debug.wasm: Cargo.toml Cargo.lock components/wit/deps $(shell find components/$1 -type f) $(shell find crates -type f 2> /dev/null) ${COMPONENTS_DIR}/$1/README.md | $(call tool,wasm-tools)
cargo build --target $(call cargo_target,$1) -p $1
ifeq ($(call cargo_target,$1),wasm32-unknown-unknown)
cargo build --target wasm32-unknown-unknown -p $1
wasm-tools component new target/wasm32-unknown-unknown/debug/$(subst -,_,$1).wasm -o ${COMPONENTS_DIR}/$1/$1.debug.wasm
else
cp target/$(call cargo_target,$1)/debug/$(subst -,_,$1).wasm ${COMPONENTS_DIR}/$1/$1.debug.wasm
endif

endif

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ The cli tools the build uses, [`wasm-tools`](https://github.com/bytecodealliance
- [`extract-wit`](./components/extract-wit/)
- [`filesystem-loader`](./components/filesystem-loader/)
- [`http-loader`](./components/http-loader/)
- [`oci-loader`](./components/oci-loader/)
- [`path-loader`](./components/path-loader/)
- [`path-loader-router`](./components/path-loader-router/)
- [`wac-loader`](./components/wac-loader/)

## Community
Expand Down
3 changes: 3 additions & 0 deletions components/dep-componentized-http-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `dep-componentized-http-client`

from https://github.com/componentized/http/tree/main/components/http-client
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ghcr.io/componentized/http/client:0.1.0-dev@sha256:cec0f0e8cf10ae936ff8b45a78ac199c0f7a8e2c0a3cb1b3e86ec6cac6ad1646
ghcr.io/componentized/http/client:0.1.0-dev_debug@sha256:bfe41f3aa3fb3329fb12c2fa8fe1d6048b95bd4fcb0d87f102f5a8d012a2a51d
3 changes: 3 additions & 0 deletions components/dep-componentized-oci-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `dep-componentized-oci-client`

from https://github.com/componentized/oci/tree/main/components/client
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ghcr.io/componentized/oci/client:0.1.0-dev@sha256:a5c60dc24dea1883e347f67a7f3929694b620c8096fb4afff54cb8b9fbc95004
ghcr.io/componentized/oci/client:0.1.0-dev_debug@sha256:075eecc73333b82ce0854907e06015296f7c3a839539d7b5457bec31d95e7a00
4 changes: 0 additions & 4 deletions components/filesystem-loader/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
cargo-features = ["per-package-target"]

[package]
name = "filesystem-loader"
version = "0.1.0"
edition = "2024"
license = "Apache-2.0"

default-target = "wasm32-wasip3"

[lib]
crate-type = ["cdylib"]
# built for the default-target, a wasm test binary is unable to run on the host
Expand Down
147 changes: 140 additions & 7 deletions components/filesystem-loader/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,156 @@
use std::{fs, io};
use std::path::Path;

use crate::{
exports::componentized::component::path_loader::Guest,
exports::componentized::component::types::{Component, Error},
exports::componentized::component::{
path_loader::Guest,
types::{Component, Error},
},
wasi::filesystem::{preopens, types as filesystem},
};

pub(crate) struct FilesystemLoader;

impl Guest for FilesystemLoader {
#[allow(async_fn_in_trait)]
async fn load(path: String) -> Result<Component, Error> {
let component = fs::read(path)?;
let (dir, path) = resolve(&path)?;
let file = dir
.open_at(
filesystem::PathFlags::SYMLINK_FOLLOW,
path,
filesystem::OpenFlags::empty(),
filesystem::DescriptorFlags::READ,
)
.await?;
let (data, result) = file.read_via_stream(0);
let component = data.collect().await;
result.await?;
Ok(component)
}
}

impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Other(Some(value.to_string()))
/// Resolve a path to the preopened directory that contains it and the path relative to that
/// directory. When multiple preopens match, the most specific one wins.
fn resolve(path: &str) -> Result<(filesystem::Descriptor, String), Error> {
let path = Path::new(path);
let mut resolved: Option<(usize, filesystem::Descriptor, String)> = None;
for (dir, name) in preopens::get_directories() {
let prefix = Path::new(&name);
let relative = if path.is_relative() && prefix == Path::new(".") {
Some(path)
} else {
path.strip_prefix(prefix).ok()
};
let Some(relative) = relative.and_then(Path::to_str) else {
continue;
};
let specificity = prefix.components().count();
if resolved.as_ref().is_none_or(|(s, _, _)| specificity > *s) {
resolved = Some((specificity, dir, relative.to_string()));
}
}
resolved.map(|(_, dir, path)| (dir, path)).ok_or_else(|| {
Error::Other(Some(format!(
"filesystem: no preopen for path {}",
path.display()
)))
})
}

impl From<filesystem::ErrorCode> for Error {
fn from(value: filesystem::ErrorCode) -> Self {
match value {
filesystem::ErrorCode::Access => Self::Other(Some("filesystem: access".to_string())),
filesystem::ErrorCode::Already => Self::Other(Some("filesystem: already".to_string())),
filesystem::ErrorCode::BadDescriptor => {
Self::Other(Some("filesystem: bad-descriptor".to_string()))
}
filesystem::ErrorCode::Busy => Self::Other(Some("filesystem: busy".to_string())),
filesystem::ErrorCode::Deadlock => {
Self::Other(Some("filesystem: deadlock".to_string()))
}
filesystem::ErrorCode::Quota => Self::Other(Some("filesystem: quota".to_string())),
filesystem::ErrorCode::Exist => Self::Other(Some("filesystem: exist".to_string())),
filesystem::ErrorCode::FileTooLarge => {
Self::Other(Some("filesystem: file-too-large".to_string()))
}
filesystem::ErrorCode::IllegalByteSequence => {
Self::Other(Some("filesystem: illegal-byte-sequence".to_string()))
}
filesystem::ErrorCode::InProgress => {
Self::Other(Some("filesystem: in-progress".to_string()))
}
filesystem::ErrorCode::Interrupted => {
Self::Other(Some("filesystem: interrupted".to_string()))
}
filesystem::ErrorCode::Invalid => Self::Other(Some("filesystem: invalid".to_string())),
filesystem::ErrorCode::Io => Self::Other(Some("filesystem: io".to_string())),
filesystem::ErrorCode::IsDirectory => {
Self::Other(Some("filesystem: is-directory".to_string()))
}
filesystem::ErrorCode::Loop => Self::Other(Some("filesystem: loop".to_string())),
filesystem::ErrorCode::TooManyLinks => {
Self::Other(Some("filesystem: too-many-links".to_string()))
}
filesystem::ErrorCode::MessageSize => {
Self::Other(Some("filesystem: message-size".to_string()))
}
filesystem::ErrorCode::NameTooLong => {
Self::Other(Some("filesystem: name-too-long".to_string()))
}
filesystem::ErrorCode::NoDevice => {
Self::Other(Some("filesystem: no-device".to_string()))
}
filesystem::ErrorCode::NoEntry => Self::Other(Some("filesystem: no-entry".to_string())),
filesystem::ErrorCode::NoLock => Self::Other(Some("filesystem: no-lock".to_string())),
filesystem::ErrorCode::InsufficientMemory => {
Self::Other(Some("filesystem: insufficient-memory".to_string()))
}
filesystem::ErrorCode::InsufficientSpace => {
Self::Other(Some("filesystem: insufficient-space".to_string()))
}
filesystem::ErrorCode::NotDirectory => {
Self::Other(Some("filesystem: not-directory".to_string()))
}
filesystem::ErrorCode::NotEmpty => {
Self::Other(Some("filesystem: not-empty".to_string()))
}
filesystem::ErrorCode::NotRecoverable => {
Self::Other(Some("filesystem: not-recoverable".to_string()))
}
filesystem::ErrorCode::Unsupported => {
Self::Other(Some("filesystem: unsupported".to_string()))
}
filesystem::ErrorCode::NoTty => Self::Other(Some("filesystem: no-tty".to_string())),
filesystem::ErrorCode::NoSuchDevice => {
Self::Other(Some("filesystem: no-such-device".to_string()))
}
filesystem::ErrorCode::Overflow => {
Self::Other(Some("filesystem: overflow".to_string()))
}
filesystem::ErrorCode::NotPermitted => {
Self::Other(Some("filesystem: not-permitted".to_string()))
}
filesystem::ErrorCode::Pipe => Self::Other(Some("filesystem: pipe".to_string())),
filesystem::ErrorCode::ReadOnly => {
Self::Other(Some("filesystem: read-only".to_string()))
}
filesystem::ErrorCode::InvalidSeek => {
Self::Other(Some("filesystem: invalid-seek".to_string()))
}
filesystem::ErrorCode::TextFileBusy => {
Self::Other(Some("filesystem: text-file-busy".to_string()))
}
filesystem::ErrorCode::CrossDevice => {
Self::Other(Some("filesystem: cross-device".to_string()))
}
filesystem::ErrorCode::Other(None) => {
Self::Other(Some("filesystem: other".to_string()))
}
filesystem::ErrorCode::Other(Some(message)) => {
Self::Other(Some(format!("filesystem: other: {message}")))
}
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions components/oci-loader/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "oci-loader"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"

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

[dependencies]
wit-bindgen = { workspace = true }
3 changes: 3 additions & 0 deletions components/oci-loader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `oci-loader`

Loads a component from an OCI registry by reference.
Loading
Loading