Skip to content
This repository has been archived by the owner on Aug 13, 2024. It is now read-only.

Commit

Permalink
Add support for run constraints (closes #45)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeemster committed Nov 29, 2016
1 parent b27781e commit fd405b8
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ rand = "0.3"
rust-crypto = "^0.2"
uuid = { version = "0.2", features = ["v4"] }
hyper = "0.9"
libc = "0.2.17"
ifaces = "0.0.3"
dns-lookup = "0.2.1"
113 changes: 112 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ extern crate rand;
extern crate crypto;
extern crate uuid;
extern crate hyper;
extern crate libc;
extern crate ifaces;
extern crate dns_lookup;

use docopt::Docopt;
use std::fs;
Expand All @@ -45,6 +48,7 @@ use std::fs::OpenOptions;
use std::env;
use hyper::Url;
use std::sync::mpsc;
use std::net;
#[cfg(test)]
use std::fs::File;
use std::collections::HashMap;
Expand All @@ -56,13 +60,15 @@ const PROC_PARSE_ERROR: i32 = 1;
const PROC_EXEC_ERROR: i32 = 2;
const PROC_OTHER_ERROR: i32 = 3;

const CONSTRAINT_HOST: &'static str = "host";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str =
"
Factotum.
Usage:
factotum run <factfile> [--start=<start_task>] [--env=<env>] [--dry-run] [--no-colour] [--webhook=<url>] [--tag=<tag>]...
factotum run <factfile> [--start=<start_task>] [--env=<env>] [--dry-run] [--no-colour] [--webhook=<url>] [--tag=<tag>]... [--constraint=<constraint>]
factotum validate <factfile> [--no-colour]
factotum dot <factfile> [--start=<start_task>] [--output=<output_file>] [--overwrite] [--no-colour]
factotum (-h | --help) [--no-colour]
Expand All @@ -79,6 +85,7 @@ Options:
--no-colour Turn off ANSI terminal colours/formatting in output.
--webhook=<url> Post updates on job execution to the specified URL.
--tag=<tag> Add job metadata (tags).
--constraint=<constraint> Checks for an external constraint that will prevent execution; allowed constraints (host).
";

#[derive(Debug, RustcDecodable)]
Expand All @@ -91,6 +98,7 @@ struct Args {
flag_dry_run: bool,
flag_no_colour: bool,
flag_tag: Option<Vec<String>>,
flag_constraint: Option<Vec<String>>,
arg_factfile: String,
flag_version: bool,
cmd_run: bool,
Expand Down Expand Up @@ -510,6 +518,94 @@ fn is_valid_url(url: &str) -> Result<(), String> {
}
}

fn get_constraint_map(constraints: &Vec<String>) -> HashMap<String,String> {
get_tag_map(constraints)
}

fn is_valid_host(host: &str) -> Result<(), String> {
if host == "*" {
return Ok(())
}

if let Ok(os_hostname) = gethostname_safe() {
if host == os_hostname {
return Ok(())
}

// Attempt to match based on the external addr
// and a dns_lookup of the hostname
if let Ok(addr) = get_external_addr() {

// Replace with std::net::lookup_host once stable
if let Ok(host_addrs) = dns_lookup::lookup_host(&host) {
for host_addr in host_addrs {
if let Ok(good_host_addr) = host_addr {
if good_host_addr == addr.ip() {
return Ok(())
}
}
}
Err("Did not find matching IP for hostname".into())
} else {
Err("Found external IP but failed to list IPs for hostname".into())
}
} else {
Err("Could not get external IP".into())
}
} else {
Err("Could not get OS hostname".into())
}
}

extern {
pub fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}

fn gethostname_safe() -> Result<String, ()> {
let len = 255;
let mut buf = Vec::<u8>::with_capacity(len);

let ptr = buf.as_mut_slice().as_mut_ptr();

let err = unsafe {
gethostname(ptr as *mut libc::c_char, len as libc::size_t)
} as libc::c_int;

match err {
0 => {
let mut real_len = len;
let mut i = 0;
loop {
let byte = unsafe { *(((ptr as u64) + (i as u64)) as *const u8) };
if byte == 0 {
real_len = i;
break;
}

i += 1;
}
unsafe { buf.set_len(real_len) }
Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
},
_ => {
Err(())
}
}
}

fn get_external_addr() -> Result<net::SocketAddr, ()> {
for iface in ifaces::Interface::get_all().unwrap().into_iter() {
if iface.kind == ifaces::Kind::Ipv4 {
if let Some(addr) = iface.addr {
if !addr.ip().is_loopback() {
return Ok(addr);
}
}
}
}
Err(())
}

fn get_tag_map(args: &Vec<String>) -> HashMap<String,String> {
let mut arg_map: HashMap<String,String> = HashMap::new();

Expand Down Expand Up @@ -622,6 +718,21 @@ fn factotum() -> i32 {
}

if args.cmd_run {
if let Some(constraints) = args.flag_constraint {
let c_map = get_constraint_map(&constraints);

if let Some(host_value) = c_map.get(CONSTRAINT_HOST) {
if let Err(msg) = is_valid_host(host_value) {
println!("{}",
format!("Error: the specifed host constraint \"{}\" is invalid. Reason: {}",
host_value,
msg)
.red());
return PROC_OTHER_ERROR;
}
}
}

if !args.flag_dry_run {
parse_file_and_execute(&args.arg_factfile,
args.flag_env,
Expand Down

0 comments on commit fd405b8

Please sign in to comment.