Skip to content

Commit

Permalink
Merge pull request #10 from cloudflavor/feature/introduce-sdk-concept
Browse files Browse the repository at this point in the history
Feature/introduce sdk concept
  • Loading branch information
oscar-automaton[bot] authored Jul 28, 2024
2 parents 3594a89 + 8f4f275 commit 3b1fa03
Show file tree
Hide file tree
Showing 20 changed files with 344 additions and 28 deletions.
2 changes: 0 additions & 2 deletions .cargo/config.toml

This file was deleted.

6 changes: 5 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ jobs:
override: true
components: rustfmt, clippy
target: wasm32-wasip1
- name: Install cargo-component
run: cargo install cargo-component
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y build-essential
- name: Install wit-bindgen
run: cargo install wit-bindgen-cli
- name: Check WIT API
run: scripts/check-wit.sh
- name: Run clippy
run: cargo clippy
- name: Build plugins
run: cargo build
run: cargo component build
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ Cargo.lock

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Gets created when running cargo component build from the root.
bindings.rs
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[workspace]
resolver = "2"
members = ["plugins/hetzner"]
members = [
"crates/plugins/hetzner",
"crates/sdk/skyforge-sdk",
"crates/sdk/skyforge-sdk-macros",
]
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# Skyforge

A registry of WASI plugins for infrastructure provisioning

## Building

All plugins are built using the wasm component of the Rust toolchain. To build the plugins, run the following command:

e.g. for the Hetzner plugin:

```sh
$ cargo install cargo-component --locked
...
$ cargo component build && cp ../../../target/wasm32-wasi/debug/hetzner.wasm ~/.config/skycrane/plugins/
...
```

This will build the plugin and copy the resulting wasm file to the
`~/.config/skycrane/plugins/` directory. The manual copy is necessary because we don't
have a registry in place at the moment. Once the registry is in place, skycrane will
download the plugin automatically.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ authors = ["Victor Palade <victor@cloudflavor.io>"]
[lib]
crate-type = ["cdylib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
wit-bindgen = "0.27.0"
skyforge-sdk = { path = "../../sdk/skyforge-sdk" }
skyforge-sdk-macros = { path = "../../sdk/skyforge-sdk-macros" }
wit-bindgen = "0.28.0"

[package.metadata.component]
package = "cloudflavor:skyforge"
4 changes: 4 additions & 0 deletions crates/plugins/hetzner/spec/base.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module(
name = "hetzner",
version = "v0.1.0",
)
4 changes: 4 additions & 0 deletions crates/plugins/hetzner/spec/network.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
network(
name = "network",
cidr = "10.0.0.1/16",
)
224 changes: 224 additions & 0 deletions crates/plugins/hetzner/src/bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Generated by `wit-bindgen` 0.25.0. DO NOT EDIT!
// Options used:
#[allow(dead_code)]
pub mod exports {
#[allow(dead_code)]
pub mod cloudflavor {
#[allow(dead_code)]
pub mod skyforge {
#[allow(dead_code, clippy::all)]
pub mod plugin_api {
#[used]
#[doc(hidden)]
#[cfg(target_arch = "wasm32")]
static __FORCE_SECTION_REF: fn() =
super::super::super::super::__link_custom_section_describing_imports;
use super::super::super::super::_rt;
#[derive(Clone)]
pub struct Config {
pub name: _rt::String,
}
impl ::core::fmt::Debug for Config {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("Config").field("name", &self.name).finish()
}
}
#[repr(u8)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Error {
InvalidFormat,
MissingField,
Other,
}
impl Error {
pub fn name(&self) -> &'static str {
match self {
Error::InvalidFormat => "invalid-format",
Error::MissingField => "missing-field",
Error::Other => "other",
}
}
pub fn message(&self) -> &'static str {
match self {
Error::InvalidFormat => "",
Error::MissingField => "",
Error::Other => "",
}
}
}
impl ::core::fmt::Debug for Error {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("Error")
.field("code", &(*self as i32))
.field("name", &self.name())
.field("message", &self.message())
.finish()
}
}
impl ::core::fmt::Display for Error {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "{} (error {})", self.name(), *self as i32)
}
}

impl std::error::Error for Error {}

impl Error {
#[doc(hidden)]
pub unsafe fn _lift(val: u8) -> Error {
if !cfg!(debug_assertions) {
return ::core::mem::transmute(val);
}

match val {
0 => Error::InvalidFormat,
1 => Error::MissingField,
2 => Error::Other,

_ => panic!("invalid enum discriminant"),
}
}
}

#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe fn _export_deserialize_config_cabi<T: Guest>(
arg0: *mut u8,
arg1: usize,
) -> *mut u8 {
#[cfg(target_arch = "wasm32")]
_rt::run_ctors_once();
let len0 = arg1;
let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0);
let result1 = T::deserialize_config(_rt::string_lift(bytes0));
let ptr2 = _RET_AREA.0.as_mut_ptr().cast::<u8>();
match result1 {
Ok(e) => {
*ptr2.add(0).cast::<u8>() = (0i32) as u8;
let Config { name: name3 } = e;
let vec4 = (name3.into_bytes()).into_boxed_slice();
let ptr4 = vec4.as_ptr().cast::<u8>();
let len4 = vec4.len();
::core::mem::forget(vec4);
*ptr2.add(8).cast::<usize>() = len4;
*ptr2.add(4).cast::<*mut u8>() = ptr4.cast_mut();
}
Err(e) => {
*ptr2.add(0).cast::<u8>() = (1i32) as u8;
*ptr2.add(4).cast::<u8>() = (e.clone() as i32) as u8;
}
};
ptr2
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe fn __post_return_deserialize_config<T: Guest>(arg0: *mut u8) {
let l0 = i32::from(*arg0.add(0).cast::<u8>());
match l0 {
0 => {
let l1 = *arg0.add(4).cast::<*mut u8>();
let l2 = *arg0.add(8).cast::<usize>();
_rt::cabi_dealloc(l1, l2, 1);
}
_ => (),
}
}
pub trait Guest {
fn deserialize_config(config_str: _rt::String) -> Result<Config, Error>;
}
#[doc(hidden)]

macro_rules! __export_cloudflavor_skyforge_plugin_api_0_1_0_cabi{
($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {

#[export_name = "cloudflavor:skyforge/plugin-api@0.1.0#deserialize-config"]
unsafe extern "C" fn export_deserialize_config(arg0: *mut u8,arg1: usize,) -> *mut u8 {
$($path_to_types)*::_export_deserialize_config_cabi::<$ty>(arg0, arg1)
}
#[export_name = "cabi_post_cloudflavor:skyforge/plugin-api@0.1.0#deserialize-config"]
unsafe extern "C" fn _post_return_deserialize_config(arg0: *mut u8,) {
$($path_to_types)*::__post_return_deserialize_config::<$ty>(arg0)
}
};);
}
#[doc(hidden)]
pub(crate) use __export_cloudflavor_skyforge_plugin_api_0_1_0_cabi;
#[repr(align(4))]
struct _RetArea([::core::mem::MaybeUninit<u8>; 12]);
static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 12]);
}
}
}
}
mod _rt {
pub use alloc_crate::string::String;

#[cfg(target_arch = "wasm32")]
pub fn run_ctors_once() {
wit_bindgen_rt::run_ctors_once();
}
pub use alloc_crate::vec::Vec;
pub unsafe fn string_lift(bytes: Vec<u8>) -> String {
if cfg!(debug_assertions) {
String::from_utf8(bytes).unwrap()
} else {
String::from_utf8_unchecked(bytes)
}
}
pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) {
if size == 0 {
return;
}
let layout = alloc::Layout::from_size_align_unchecked(size, align);
alloc::dealloc(ptr as *mut u8, layout);
}
extern crate alloc as alloc_crate;
pub use alloc_crate::alloc;
}

/// Generates `#[no_mangle]` functions to export the specified type as the
/// root implementation of all generated traits.
///
/// For more information see the documentation of `wit_bindgen::generate!`.
///
/// ```rust
/// # macro_rules! export{ ($($t:tt)*) => (); }
/// # trait Guest {}
/// struct MyType;
///
/// impl Guest for MyType {
/// // ...
/// }
///
/// export!(MyType);
/// ```
#[allow(unused_macros)]
#[doc(hidden)]

macro_rules! __export_skyforge_api_impl {
($ty:ident) => (self::export!($ty with_types_in self););
($ty:ident with_types_in $($path_to_types_root:tt)*) => (
$($path_to_types_root)*::exports::cloudflavor::skyforge::plugin_api::__export_cloudflavor_skyforge_plugin_api_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::cloudflavor::skyforge::plugin_api);
)
}
#[doc(inline)]
pub(crate) use __export_skyforge_api_impl as export;

#[cfg(target_arch = "wasm32")]
#[link_section = "component-type:wit-bindgen:0.25.0:skyforge-api:encoded world"]
#[doc(hidden)]
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 342] = *b"\
\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xd3\x01\x01A\x02\x01\
A\x02\x01B\x07\x01r\x01\x04names\x04\0\x06config\x03\0\0\x01m\x03\x0einvalid-for\
mat\x0dmissing-field\x05other\x04\0\x05error\x03\0\x02\x01j\x01\x01\x01\x03\x01@\
\x01\x0aconfig-strs\0\x04\x04\0\x12deserialize-config\x01\x05\x04\x01%cloudflavo\
r:skyforge/plugin-api@0.1.0\x05\0\x04\x01'cloudflavor:skyforge/skyforge-api@0.1.\
0\x04\0\x0b\x12\x01\0\x0cskyforge-api\x03\0\0\0G\x09producers\x01\x0cprocessed-b\
y\x02\x0dwit-component\x070.208.1\x10wit-bindgen-rust\x060.25.0";

#[inline(never)]
#[doc(hidden)]
#[cfg(target_arch = "wasm32")]
pub fn __link_custom_section_describing_imports() {
wit_bindgen_rt::maybe_link_cabi_realloc();
}
11 changes: 11 additions & 0 deletions crates/plugins/hetzner/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use skyforge_sdk::skyforge_plugin;

struct Hetzner;

#[skyforge_plugin]
impl SkyforgePlugin for Hetzner {
fn deserialize_config_impl(config: String) -> Result<Config, Error> {
println!("Hetzner config: {}", config);
Ok(Config { name: config })
}
}
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions crates/plugins/hetzner/wit
12 changes: 12 additions & 0 deletions crates/sdk/skyforge-sdk-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "skyforge-sdk-macros"
version = "0.1.0"
edition = "2021"

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "2.0"

[lib]
proc-macro = true
39 changes: 39 additions & 0 deletions crates/sdk/skyforge-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemImpl};

#[proc_macro_attribute]
pub fn skyforge_plugin(_: TokenStream, input: TokenStream) -> TokenStream {
let impl_trait = parse_macro_input!(input as ItemImpl);
let name = impl_trait.self_ty;
if let Some((_, path, _)) = &impl_trait.trait_ {
let trait_name = path.get_ident().unwrap();
if trait_name != "SkyforgePlugin" {
panic!("Expected trait name to be `SkyforgePlugin`");
}
}

let trait_items = &impl_trait.items;

let expanded = quote! {

wit_bindgen::generate!({path: "wit", world: "skyforge-api"});

use exports::cloudflavor::skyforge::plugin_api::{Guest, Config,Error};

impl Guest for #name {
fn deserialize_config(config: String) -> Result<Config, Error> {
let config = Self::deserialize_config_impl(config).unwrap();
Ok(config)
}
}

export!(#name);

impl #name {
#(#trait_items)*
}
};

TokenStream::from(expanded)
}
8 changes: 8 additions & 0 deletions crates/sdk/skyforge-sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "skyforge-sdk"
version = "0.1.0"
edition = "2021"

[dependencies]
wit-bindgen = "0.28.0"
skyforge-sdk-macros = { path = "../skyforge-sdk-macros" }
6 changes: 6 additions & 0 deletions crates/sdk/skyforge-sdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub use skyforge_sdk_macros::skyforge_plugin;

wit_bindgen::generate!({
path: "wit",
world: "skyforge-api",
});
1 change: 1 addition & 0 deletions crates/sdk/skyforge-sdk/wit
Loading

0 comments on commit 3b1fa03

Please sign in to comment.