wasmcloud/src/bin/executor.rs

140 lines
4.0 KiB
Rust

#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate tracing;
use diesel::prelude::*;
use std::{
env, fs, io,
path::PathBuf,
process::{self, Output},
time,
};
use uuid::Uuid;
use wasmcloud_api::api::Error::InternalServerError;
use wasmcloud_api::{
api::{
Error::{Database, Impossible, Subcommand},
Result,
},
models, schema, MainDatabase,
};
// Name your user agent after your app?
pub static APP_USER_AGENT: &str = concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION"),
" +https://tulpa.dev/wasmcloud/wasmcloud",
);
pub static TEMP_FOLDER: &str = concat!(
"/tmp/",
env!("CARGO_PKG_NAME"),
"-",
env!("CARGO_PKG_VERSION"),
"/"
);
#[instrument(skip(config), err)]
fn execute(
handler_id: Uuid,
config: Vec<models::HandlerConfig>,
handler_path: PathBuf,
) -> Result<(Output, time::Duration)> {
let mut child = process::Command::new("/usr/bin/env");
let child = child.arg("pahi");
let child = child.arg(handler_path);
let mut child = child.env("HANDLER_ID", handler_id.to_string());
for kv in config.into_iter() {
child = child.env(kv.key_name, kv.value_contents);
}
debug!("running");
let start = time::Instant::now();
let output = child.output().map_err(Subcommand)?;
let duration = start.elapsed();
Ok((output, duration))
}
#[get("/run/<handler_name>")]
#[instrument(skip(conn), err)]
fn schedule(handler_name: String, conn: MainDatabase) -> Result {
fs::create_dir_all(TEMP_FOLDER)?;
let hdl = {
use schema::handlers::dsl::{handlers, human_name};
handlers
.filter(human_name.eq(handler_name))
.first::<models::Handler>(&*conn)
.map_err(Database)
}?;
let cfg = {
use schema::handler_config::dsl::{handler_config, handler_id};
handler_config
.filter(handler_id.eq(hdl.id.clone()))
.load::<models::HandlerConfig>(&*conn)
.map_err(Database)
}?;
let u = url::Url::parse(&hdl.current_version.ok_or(Impossible)?).map_err(|_| Impossible)?;
debug!("{:?}", u.host_str().ok_or(Impossible)?);
// https://cdn.christine.website/file/christine-static/stickers/mara/hacker.png
let hdl_url = format!(
"https://cdn.christine.website/file/wasmcloud-modules/{}",
u.host_str().ok_or(Impossible)?
);
let fname = format!("{}{}", TEMP_FOLDER, u.host_str().unwrap());
debug!(url = &hdl_url[..], fname = &fname[..], "downloading module");
let resp = ureq::get(&hdl_url).set("User-Agent", APP_USER_AGENT).call();
if resp.ok() {
let mut fout = fs::File::create(&fname).map_err(|why| {
error!("can't make file: {}", why);
Subcommand(why)
})?;
io::copy(&mut resp.into_reader(), &mut fout).map_err(|why| Subcommand(why))?;
} else {
error!("while fetching url: {}", resp.status_line());
return Err(Impossible);
}
let (output, duration) = execute(hdl.id, cfg, fname.into()).map_err(|why| {
error!("error running module: {}", why);
InternalServerError(why.into())
})?;
info!(
duration = duration.as_millis() as i64,
module = u.path(),
"execution finished"
);
diesel::insert_into(schema::executions::table)
.values(&models::NewExecution {
handler_id: hdl.id,
finished: true,
stderr: Some(String::from_utf8(output.stderr).map_err(|_| Impossible)?), // XXX(Cadey): this is not impossible
execution_time: duration.as_millis() as i32,
})
.execute(&*conn)
.map_err(Database)?;
Ok(())
}
fn main() -> color_eyre::eyre::Result<()> {
color_eyre::install()?;
tracing_subscriber::fmt::init();
std::env::set_var("ROCKET_PORT", "8001"); // XXX(Cadey): so I can test both on my machine at once
rocket::ignite()
.attach(MainDatabase::fairing())
.mount("/", routes![schedule])
.launch();
Ok(())
}