most of git delivery, gotta refactor the whole thing for body tho
This commit is contained in:
parent
afe94b53d1
commit
03e965791f
5 changed files with 91 additions and 44 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -187,6 +187,7 @@ name = "dip"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -14,6 +14,7 @@ sha-1 = "0.7"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
|
futures = "0.1"
|
||||||
hyper = "0.12"
|
hyper = "0.12"
|
||||||
lazy_static = "1.1"
|
lazy_static = "1.1"
|
||||||
notify = "4.0"
|
notify = "4.0"
|
||||||
|
|
|
@ -13,6 +13,7 @@ extern crate structopt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use failure::{err_msg, Error};
|
use failure::{err_msg, Error};
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
|
@ -31,6 +32,7 @@ struct Opt {
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct Config {
|
struct Config {
|
||||||
secret: String,
|
secret: String,
|
||||||
|
outdir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -45,7 +47,9 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
let mut payload = String::new();
|
let mut payload = String::new();
|
||||||
io::stdin().read_to_string(&mut payload)?;
|
io::stdin().read_to_string(&mut payload)?;
|
||||||
|
println!("raw payload: {}", payload);
|
||||||
let payload: Payload = serde_json::from_str(&payload)?;
|
let payload: Payload = serde_json::from_str(&payload)?;
|
||||||
|
println!("processed payload: {}", payload.body);
|
||||||
|
|
||||||
let secret = GenericArray::from_iter(config.secret.bytes());
|
let secret = GenericArray::from_iter(config.secret.bytes());
|
||||||
let mut mac = Hmac::<Sha1>::new(&secret);
|
let mut mac = Hmac::<Sha1>::new(&secret);
|
||||||
|
@ -60,13 +64,13 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
let auth = payload
|
let auth = payload
|
||||||
.headers
|
.headers
|
||||||
.get("X-Hub-Signature")
|
.get("x-hub-signature")
|
||||||
.ok_or(err_msg("Missing auth header"))?;
|
.ok_or(err_msg("Missing auth header"))?;
|
||||||
|
|
||||||
let left = SecStr::from(format!("sha1={}", signature));
|
let left = SecStr::from(format!("sha1={}", signature));
|
||||||
let right = SecStr::from(auth.bytes().collect::<Vec<_>>());
|
let right = SecStr::from(auth.bytes().collect::<Vec<_>>());
|
||||||
assert!(left == right, "HMAC signature didn't match");
|
assert!(left == right, "HMAC signature didn't match");
|
||||||
|
|
||||||
println!("{}", payload.body);
|
println!("gonna clone it to {:?}", config.outdir);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
use failure::{err_msg, Error};
|
use failure::{err_msg, Error};
|
||||||
use serde_json::Value as JsonValue;
|
use serde::Serialize;
|
||||||
|
use serde_json::{Serializer as JsonSerializer, Value as JsonValue};
|
||||||
use toml::Value as TomlValue;
|
use toml::Value as TomlValue;
|
||||||
|
|
||||||
use PROGRAMS;
|
use PROGRAMS;
|
||||||
|
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
|
config: TomlValue,
|
||||||
exec: PathBuf,
|
exec: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,21 +28,46 @@ impl Handler {
|
||||||
.ok_or(err_msg(format!("'{}' is not a valid executable", handler)))
|
.ok_or(err_msg(format!("'{}' is not a valid executable", handler)))
|
||||||
.map(|value| value.clone())?
|
.map(|value| value.clone())?
|
||||||
};
|
};
|
||||||
Ok(Handler { exec })
|
let config = config.clone();
|
||||||
|
Ok(Handler { config, exec })
|
||||||
}
|
}
|
||||||
pub fn run(&self, _: Result<JsonValue, Error>) -> Result<JsonValue, Error> {
|
pub fn run(&self, input: JsonValue) -> Result<JsonValue, Error> {
|
||||||
Command::new(&self.exec)
|
let config = {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
{
|
||||||
|
let mut serializer = JsonSerializer::new(&mut buf);
|
||||||
|
TomlValue::serialize(&self.config, &mut serializer)?;
|
||||||
|
}
|
||||||
|
String::from_utf8(buf).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut child = Command::new(&self.exec)
|
||||||
.env("DIP_ROOT", "")
|
.env("DIP_ROOT", "")
|
||||||
.output()
|
.arg("--config")
|
||||||
.map_err(|err| err_msg(format!("{}", err)))
|
.arg(config)
|
||||||
.and_then(|output| {
|
.stdin(Stdio::piped())
|
||||||
if !output.status.success() {
|
.stdout(Stdio::piped())
|
||||||
return Err(err_msg(format!(
|
.stderr(Stdio::piped())
|
||||||
"'{:?}' returned with a non-zero status code: {}",
|
.spawn()?;
|
||||||
self.exec, output.status
|
{
|
||||||
)));
|
match child.stdin {
|
||||||
|
Some(ref mut stdin) => {
|
||||||
|
write!(stdin, "{}", input)?;
|
||||||
}
|
}
|
||||||
Ok(json!({}))
|
None => bail!("done fucked"),
|
||||||
})
|
};
|
||||||
|
}
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
if !output.status.success() {
|
||||||
|
// TODO: get rid of unwraps
|
||||||
|
return Err(err_msg(format!(
|
||||||
|
"'{:?}' returned with a non-zero status code: {}\nstdout:\n{}\nstderr:\n{}",
|
||||||
|
self.exec,
|
||||||
|
output.status,
|
||||||
|
String::from_utf8(output.stdout).unwrap(),
|
||||||
|
String::from_utf8(output.stderr).unwrap()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(json!({}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
67
src/lib.rs
67
src/lib.rs
|
@ -1,7 +1,9 @@
|
||||||
//! # Dip
|
//! # Dip
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate futures;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
@ -27,7 +29,7 @@ use std::time::Duration;
|
||||||
use failure::{err_msg, Error};
|
use failure::{err_msg, Error};
|
||||||
use hyper::rt::Future;
|
use hyper::rt::Future;
|
||||||
use hyper::service::service_fn_ok;
|
use hyper::service::service_fn_ok;
|
||||||
use hyper::{Body, Request, Response, Server};
|
use hyper::{Body, Request, Response, Server, StatusCode};
|
||||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
@ -45,22 +47,12 @@ lazy_static! {
|
||||||
static ref HOOKS: Mutex<HashMap<String, Hook>> = Mutex::new(HashMap::new());
|
static ref HOOKS: Mutex<HashMap<String, Hook>> = Mutex::new(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
const NOTFOUND: &str = r#"<html>
|
const NOTFOUND: &str = "<html> <head> <style> * { font-family: sans-serif; } body { padding: 20px 60px; } </style> </head> <body> <h1>Looks like you took a wrong turn!</h1> <p>There's nothing to see here.</p> </body> </html>";
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
* { font-family: sans-serif; }
|
|
||||||
body { padding: 20px 60px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Looks like you took a wrong turn!</h1>
|
|
||||||
<p>There's nothing to see here.</p>
|
|
||||||
</body>
|
|
||||||
</html>"#;
|
|
||||||
|
|
||||||
fn service_fn(req: &Request<Body>) -> Result<Response<Body>, Error> {
|
fn service_fn(req: Request<Body>) -> Result<Response<Body>, Error> {
|
||||||
|
let path = req.uri().path().to_owned();
|
||||||
let captures = URIPATTERN
|
let captures = URIPATTERN
|
||||||
.captures(req.uri().path())
|
.captures(path.as_ref())
|
||||||
.ok_or(err_msg("Did not match url pattern"))?;
|
.ok_or(err_msg("Did not match url pattern"))?;
|
||||||
let name = captures
|
let name = captures
|
||||||
.name("name")
|
.name("name")
|
||||||
|
@ -70,22 +62,41 @@ fn service_fn(req: &Request<Body>) -> Result<Response<Body>, Error> {
|
||||||
let hook = hooks
|
let hook = hooks
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or(err_msg(format!("Hook '{}' doesn't exist", name)))?;
|
.ok_or(err_msg(format!("Hook '{}' doesn't exist", name)))?;
|
||||||
let req_obj = json!({
|
|
||||||
"method": req.method().as_str(),
|
let req_obj = {
|
||||||
});
|
let headers = req
|
||||||
|
.headers()
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(k, v)| {
|
||||||
|
let key = k.unwrap().as_str().to_owned();
|
||||||
|
v.to_str().map(|value| (key, value.to_owned())).ok()
|
||||||
|
}).collect::<HashMap<_, _>>();
|
||||||
|
let method = req.method().as_str().to_owned();
|
||||||
|
// probably not idiomatically the best way to do it
|
||||||
|
// i was just trying to get something working
|
||||||
|
let body = "wip".to_owned();
|
||||||
|
json!({
|
||||||
|
"body": body,
|
||||||
|
"headers": headers,
|
||||||
|
"method": method,
|
||||||
|
})
|
||||||
|
};
|
||||||
hook.iter()
|
hook.iter()
|
||||||
.fold(Ok(req_obj), |prev, handler| handler.run(prev))
|
.fold(Ok(req_obj), |prev, handler| {
|
||||||
.map(|_| Response::new(Body::from("success")))
|
prev.and_then(|val| handler.run(val))
|
||||||
|
}).map(|_| Response::new(Body::from("success")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn service_fn_wrapper(req: Request<Body>) -> Response<Body> {
|
fn service_fn_wrapper(req: Request<Body>) -> Response<Body> {
|
||||||
match service_fn(&req) {
|
let uri = req.uri().path().to_owned();
|
||||||
Ok(response) => response,
|
service_fn(req).unwrap_or_else(|err| {
|
||||||
Err(err) => {
|
eprintln!("Error from '{}': {}", uri, err);
|
||||||
eprintln!("Got error from '{}': {}", req.uri().path(), err);
|
Response::builder()
|
||||||
Response::new(Body::from(NOTFOUND))
|
.status(StatusCode::NOT_FOUND)
|
||||||
}
|
.body(Body::from(NOTFOUND))
|
||||||
}
|
.unwrap()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_config<P>(root: P)
|
fn load_config<P>(root: P)
|
||||||
|
@ -96,6 +107,7 @@ where
|
||||||
// hold on to the lock while config is being reloaded
|
// hold on to the lock while config is being reloaded
|
||||||
{
|
{
|
||||||
let mut programs = PROGRAMS.lock().unwrap();
|
let mut programs = PROGRAMS.lock().unwrap();
|
||||||
|
// TODO: some kind of smart diff
|
||||||
programs.clear();
|
programs.clear();
|
||||||
let programs_dir = {
|
let programs_dir = {
|
||||||
let mut p = root.as_ref().to_path_buf();
|
let mut p = root.as_ref().to_path_buf();
|
||||||
|
@ -168,6 +180,7 @@ where
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// for now, naively reload entire config every time
|
// for now, naively reload entire config every time
|
||||||
|
// TODO: don't do this
|
||||||
load_config(root.as_ref())
|
load_config(root.as_ref())
|
||||||
}
|
}
|
||||||
Err(e) => println!("watch error: {:?}", e),
|
Err(e) => println!("watch error: {:?}", e),
|
||||||
|
|
Loading…
Reference in a new issue