diff --git a/Cargo.lock b/Cargo.lock index 4ab269fbc..17ed9210b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6380,7 +6380,6 @@ dependencies = [ "clap 4.0.27", "hyper", "rmp-serde", - "serenity", "shuttle-common", "shuttle-proto", "shuttle-service", @@ -8747,15 +8746,3 @@ dependencies = [ "cc", "libc", ] - -[[patch.unused]] -name = "shuttle-aws-rds" -version = "0.8.0" - -[[patch.unused]] -name = "shuttle-persist" -version = "0.8.0" - -[[patch.unused]] -name = "shuttle-shared-db" -version = "0.8.0" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index a790bd94a..d9b757938 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -13,7 +13,6 @@ cap-std = "0.26.0" clap ={ version = "4.0.18", features = ["derive"] } hyper = { version = "0.14.23", features = ["full"] } rmp-serde = { version = "1.1.1" } -serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } thiserror = { workspace = true } tokio = { version = "=1.22.0", features = ["full"] } tokio-stream = "0.1.11" diff --git a/runtime/Makefile b/runtime/Makefile index 51f33e409..a706e8146 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -1,16 +1,12 @@ -.PHONY: axum serenity +.PHONY: axum -all: axum serenity +all: axum axum: cd ../tmp/axum-wasm; cargo build --target wasm32-wasi cp ../tmp/axum-wasm/target/wasm32-wasi/debug/shuttle_axum.wasm axum.wasm -serenity: - cd ../tmp/wasm; cargo build --target wasm32-wasi - cp ../tmp/wasm/target/wasm32-wasi/debug/shuttle_serenity.wasm serenity.wasm - -test: serenity axum +test: axum cargo test --all-features -- --nocapture runtime: diff --git a/runtime/README.md b/runtime/README.md index c78b61f19..e0ea4da9f 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -1,48 +1,5 @@ # How to run -## serenity-wasm - -Build and run: - -## shuttle-next - -Compile the shuttle-next serenity runtime: - -```bash -make serenity -``` - -Run the test: - -```bash -cargo test serenity -- --nocapture - -# or, run tests for both axum and serenity: -make test -``` - -Start the shuttle-next runtime: - -```bash -DISCORD_TOKEN=xxx cargo run -``` - -In another terminal: - -``` bash -# load wasm module -grpcurl -plaintext -import-path ../proto -proto runtime.proto -d '{"service_name": "Tonic", "path": "/home//runtime/serenity.wasm"}' localhost:6001 runtime.Runtime/Load - -# start bot (the deployment ID is needed in the StartRequest, but it isn't used by the serenity bot currently) -grpcurl -plaintext -import-path ../proto -proto runtime.proto -d '{"service_name": "Tonic", "deployment_id": "MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAw"}' localhost:6001 runtime.Runtime/Start - -# subscribe to logs (unimplemented) -grpcurl -plaintext -import-path ../proto -proto runtime.proto localhost:6001 runtime.Runtime/SubscribeLogs - -# stop (the deployment ID is needed in the StopRequest, but it isn't used by the serenity bot currently) -grpcurl -plaintext -import-path ../proto -proto runtime.proto -d '{"service_name": "Tonic", "deployment_id": "MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAw"}' localhost:6001 runtime.Runtime/Stop -``` - ## axum-wasm Compile the wasm axum router: @@ -56,7 +13,7 @@ Run the test: ```bash cargo test axum -- --nocapture -# or, run tests for both axum and serenity: +# or, run tests make test ``` diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1119bf890..81d7acaef 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,10 +1,8 @@ mod args; mod axum; mod legacy; -mod next; pub mod provisioner_factory; pub use args::Args; pub use axum::AxumWasm; pub use legacy::Legacy; -pub use next::Next; diff --git a/runtime/src/main.rs b/runtime/src/main.rs index f01d95ac0..3c4f64cb5 100644 --- a/runtime/src/main.rs +++ b/runtime/src/main.rs @@ -5,7 +5,7 @@ use std::{ use clap::Parser; use shuttle_proto::runtime::runtime_server::RuntimeServer; -use shuttle_runtime::{Args, AxumWasm, Legacy, Next}; +use shuttle_runtime::{Args, AxumWasm, Legacy}; use tonic::transport::Server; use tracing::trace; use tracing_subscriber::{fmt, prelude::*, EnvFilter}; @@ -41,9 +41,7 @@ async fn main() { let svc = RuntimeServer::new(axum); server_builder.add_service(svc) } else { - let next = Next::default(); - let svc = RuntimeServer::new(next); - server_builder.add_service(svc) + panic!("No runtime was selected"); }; router.serve(addr).await.unwrap(); diff --git a/runtime/src/next/mod.rs b/runtime/src/next/mod.rs deleted file mode 100644 index da703189a..000000000 --- a/runtime/src/next/mod.rs +++ /dev/null @@ -1,277 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::{Read, Write}; -use std::ops::DerefMut; -use std::os::unix::prelude::RawFd; -use std::path::Path; -use std::str::FromStr; -use std::sync::Arc; - -use async_trait::async_trait; -use cap_std::os::unix::net::UnixStream; -use serenity::{model::prelude::*, prelude::*}; -use shuttle_proto::runtime::runtime_server::Runtime; -use shuttle_proto::runtime::{ - self, LoadRequest, LoadResponse, StartRequest, StartResponse, StopRequest, StopResponse, - SubscribeLogsRequest, -}; -use shuttle_service::ServiceName; -use tokio::sync::oneshot; -use tokio_stream::wrappers::ReceiverStream; -use tonic::{Request, Response, Status}; -use tracing::{error, trace}; -use wasi_common::file::FileCaps; -use wasmtime::{Engine, Linker, Module, Store}; -use wasmtime_wasi::sync::net::UnixStream as WasiUnixStream; -use wasmtime_wasi::{WasiCtx, WasiCtxBuilder}; - -pub struct Next { - bot: std::sync::Mutex>, - kill_tx: std::sync::Mutex>>, -} - -impl Next { - pub fn new() -> Self { - Self { - bot: std::sync::Mutex::new(None), - kill_tx: std::sync::Mutex::new(None), - } - } -} - -impl Default for Next { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl Runtime for Next { - async fn load(&self, request: Request) -> Result, Status> { - let wasm_path = request.into_inner().path; - trace!(wasm_path, "loading"); - - let bot = Bot::new(wasm_path); - - *self.bot.lock().unwrap() = Some(bot); - - let message = LoadResponse { success: true }; - - Ok(Response::new(message)) - } - - async fn start( - &self, - _request: Request, - ) -> Result, Status> { - let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let token = env::var("DISCORD_TOKEN").unwrap(); - let bot: Bot = { - let guard = self.bot.lock().unwrap(); - guard.as_ref().unwrap().clone() - }; - let client = bot.into_client(token.as_str(), intents).await; - - let (kill_tx, kill_rx) = tokio::sync::oneshot::channel(); - - *self.kill_tx.lock().unwrap() = Some(kill_tx); - - // start bot as a background task with a kill receiver - trace!("starting bot"); - tokio::spawn(run_until_stopped(client, kill_rx)); - - let message = StartResponse { - success: true, - // todo: port set here until I can set the port field to optional in the protobuf - port: 8001, - }; - - Ok(Response::new(message)) - } - - type SubscribeLogsStream = ReceiverStream>; - - async fn subscribe_logs( - &self, - _request: Request, - ) -> Result, Status> { - todo!() - } - - async fn stop(&self, request: Request) -> Result, Status> { - let request = request.into_inner(); - - let service_name = ServiceName::from_str(request.service_name.as_str()) - .map_err(|err| Status::from_error(Box::new(err)))?; - - let kill_tx = self.kill_tx.lock().unwrap().deref_mut().take(); - - if let Some(kill_tx) = kill_tx { - if kill_tx - .send(format!("stopping deployment: {}", &service_name)) - .is_err() - { - error!("the receiver dropped"); - return Err(Status::internal("failed to stop deployment")); - } - - Ok(Response::new(StopResponse { success: true })) - } else { - Err(Status::internal("failed to stop deployment")) - } - } -} - -/// Run the bot until a stop signal is received -async fn run_until_stopped(mut client: Client, kill_rx: tokio::sync::oneshot::Receiver) { - tokio::select! { - _ = client.start() => { - trace!("serenity bot stopped"); - }, - message = kill_rx => { - match message { - Ok(msg) => trace!("{msg}"), - Err(_) => trace!("the sender dropped") - } - } - } -} - -struct BotBuilder { - engine: Engine, - store: Store, - linker: Linker, - src: Option, -} - -impl BotBuilder { - pub fn new() -> Self { - let engine = Engine::default(); - - let mut linker: Linker = Linker::new(&engine); - wasmtime_wasi::add_to_linker(&mut linker, |s| s).unwrap(); - - let wasi = WasiCtxBuilder::new() - .inherit_stdio() - .inherit_args() - .unwrap() - .build(); - - let store = Store::new(&engine, wasi); - - Self { - engine, - store, - linker, - src: None, - } - } - - pub fn src>(mut self, src: P) -> Self { - self.src = Some(File::open(src).unwrap()); - self - } - - pub fn build(mut self) -> Bot { - let mut buf = Vec::new(); - self.src.unwrap().read_to_end(&mut buf).unwrap(); - let module = Module::new(&self.engine, buf).unwrap(); - - for export in module.exports() { - println!("export: {}", export.name()); - } - - self.linker.module(&mut self.store, "bot", &module).unwrap(); - let inner = BotInner { - store: self.store, - linker: self.linker, - }; - Bot { - inner: Arc::new(Mutex::new(inner)), - } - } -} - -struct BotInner { - store: Store, - linker: Linker, -} - -impl BotInner { - pub async fn message(&mut self, new_message: &str) -> Option { - let (mut host, client) = UnixStream::pair().unwrap(); - let client = WasiUnixStream::from_cap_std(client); - - self.store - .data_mut() - .insert_file(3, Box::new(client), FileCaps::all()); - - host.write_all(new_message.as_bytes()).unwrap(); - host.write_all(&[0]).unwrap(); - - println!("calling inner EventHandler message"); - self.linker - .get(&mut self.store, "bot", "__SHUTTLE_EventHandler_message") - .unwrap() - .into_func() - .unwrap() - .typed::(&self.store) - .unwrap() - .call(&mut self.store, 3) - .unwrap(); - - let mut resp = String::new(); - host.read_to_string(&mut resp).unwrap(); - - if resp.is_empty() { - None - } else { - Some(resp) - } - } -} - -#[derive(Clone)] -struct Bot { - inner: Arc>, -} - -impl Bot { - pub fn builder() -> BotBuilder { - BotBuilder::new() - } - - pub fn new>(src: P) -> Self { - Self::builder().src(src).build() - } - - pub async fn into_client(self, token: &str, intents: GatewayIntents) -> Client { - Client::builder(token, intents) - .event_handler(self) - .await - .unwrap() - } -} - -#[async_trait] -impl EventHandler for Bot { - async fn message(&self, ctx: Context, new_message: Message) { - let mut inner = self.inner.lock().await; - if let Some(resp) = inner.message(new_message.content.as_str()).await { - new_message.channel_id.say(&ctx.http, resp).await.unwrap(); - } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - - #[tokio::test] - async fn serenity() { - let bot = Bot::new("serenity.wasm"); - let mut inner = bot.inner.lock().await; - assert_eq!(inner.message("not !hello").await, None); - assert_eq!(inner.message("!hello").await, Some("world!".to_string())); - } -} diff --git a/tmp/wasm/Cargo.toml b/tmp/wasm/Cargo.toml deleted file mode 100644 index b935b0340..000000000 --- a/tmp/wasm/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "shuttle-serenity" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = [ "cdylib" ] - -[dependencies] diff --git a/tmp/wasm/src/lib.rs b/tmp/wasm/src/lib.rs deleted file mode 100644 index 74c859641..000000000 --- a/tmp/wasm/src/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::fs::File; -use std::io::{Read, Write}; -use std::os::wasi::prelude::*; - -pub fn handle_message(message: &str) -> Option { - if message == "!hello" { - Some("world!".to_string()) - } else { - None - } -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn __SHUTTLE_EventHandler_message(fd: RawFd) { - println!("inner handler awoken; interacting with fd={fd}"); - - let mut f = unsafe { File::from_raw_fd(fd) }; - - let mut buf = Vec::new(); - let mut c_buf = [0; 1]; - loop { - f.read(&mut c_buf).unwrap(); - if c_buf[0] == 0 { - break; - } else { - buf.push(c_buf[0]); - } - } - - let msg = String::from_utf8(buf).unwrap(); - println!("got message: {msg}"); - - if let Some(resp) = handle_message(msg.as_str()) { - f.write_all(resp.as_bytes()).unwrap(); - } -}