Skip to content

Commit

Permalink
shuttle next wrapper POC (#431)
Browse files Browse the repository at this point in the history
* feat: runtime skeleton

* feat: cleanup

* feat: update cargo.lock, sort deps

* fix: typo
  • Loading branch information
oddgrd authored Oct 25, 2022
1 parent a1c5fc5 commit f7e09b6
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 23 deletions.
76 changes: 53 additions & 23 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"common",
"deployer",
"gateway",
"next",
"proto",
"provisioner",
"service"
Expand Down
24 changes: 24 additions & 0 deletions next/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "shuttle-next"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = "0.1.58"
clap ={ version = "4.0.18", features = ["derive"] }
thiserror = "1.0.37"
tokio = { version = "=1.20.1", features = ["full"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }

[dependencies.shuttle-common]
version = "0.7.0"
path = "../common"

[dependencies.shuttle-service]
version = "0.7.0"
default-features = false
features = ["loader"]
path = "../service"
9 changes: 9 additions & 0 deletions next/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## shuttle-next runtime-wrapper

Load and run an .so library that implements `shuttle_service::Service`.

To load and run, pass the path to the .so file to load as an argument to the shuttle-next binary:

```bash
cargo run -- -f "src/libhello_world.so"
```
8 changes: 8 additions & 0 deletions next/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use clap::Parser;

#[derive(Parser, Debug)]
pub struct Args {
/// Uri to the `.so` file to load
#[arg(long, short)]
pub file_path: String,
}
12 changes: 12 additions & 0 deletions next/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use shuttle_service::loader::LoaderError;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("Load error: {0}")]
Load(#[from] LoaderError),
#[error("Run error: {0}")]
Run(#[from] shuttle_service::Error),
}

pub type Result<T> = std::result::Result<T, Error>;
2 changes: 2 additions & 0 deletions next/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod args;
pub mod error;
103 changes: 103 additions & 0 deletions next/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::{collections::BTreeMap, net::SocketAddr, path::PathBuf, str::FromStr};

use async_trait::async_trait;
use clap::Parser;
use shuttle_common::{database, LogItem};
use shuttle_next::args::Args;
use shuttle_service::{
loader::{LoadedService, Loader},
Factory, Logger, ServiceName,
};
use tokio::sync::mpsc::{self, UnboundedReceiver};
use tracing::{info, instrument, trace};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};

#[tokio::main(flavor = "multi_thread")]
async fn main() {
let args = Args::parse();

let fmt_layer = fmt::layer();
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("info"))
.unwrap();

tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();

trace!(args = ?args, "parsed args");

let address: SocketAddr = "127.0.0.1:8000".parse().unwrap();
let mut factory = DummyFactory::new();
let (logger, _rx) = get_logger();
let so_path = PathBuf::from(args.file_path.as_str());

let service = load_service(address, so_path, &mut factory, logger)
.await
.unwrap();

_ = tokio::spawn(run(service, address)).await;
}

#[instrument(skip(service))]
async fn run(service: LoadedService, addr: SocketAddr) {
let (handle, library) = service;

info!("starting deployment on {}", addr);
handle.await.unwrap().unwrap();

tokio::spawn(async move {
trace!("closing .so file");
library.close().unwrap();
});
}

#[instrument(skip(addr, so_path, factory, logger))]
async fn load_service(
addr: SocketAddr,
so_path: PathBuf,
factory: &mut dyn Factory,
logger: Logger,
) -> shuttle_next::error::Result<LoadedService> {
let loader = Loader::from_so_file(so_path)?;

Ok(loader.load(factory, addr, logger).await?)
}

struct DummyFactory {
service_name: ServiceName,
}

impl DummyFactory {
fn new() -> Self {
Self {
service_name: ServiceName::from_str("next").unwrap(),
}
}
}

#[async_trait]
impl Factory for DummyFactory {
fn get_service_name(&self) -> ServiceName {
self.service_name.clone()
}

async fn get_db_connection_string(
&mut self,
_: database::Type,
) -> Result<String, shuttle_service::Error> {
todo!()
}

async fn get_secrets(&mut self) -> Result<BTreeMap<String, String>, shuttle_service::Error> {
todo!()
}
}

fn get_logger() -> (Logger, UnboundedReceiver<LogItem>) {
let (tx, rx) = mpsc::unbounded_channel();
let logger = Logger::new(tx, Default::default());

(logger, rx)
}

0 comments on commit f7e09b6

Please sign in to comment.