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

Commit

Permalink
Fix behaviour on dot --overwrite (closes #97)
Browse files Browse the repository at this point in the history
When the --overwrite flag is used it should not fail if the file does not exist. It should instead
create the file as normal. This is similar to '>' when piping data to a file (in a shell).
  • Loading branch information
ninjabear committed Mar 16, 2017
1 parent 15c253c commit 680e90d
Showing 1 changed file with 58 additions and 36 deletions.
94 changes: 58 additions & 36 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ fn parse_file_and_execute(factfile: &str,
env: Option<String>,
start_from: Option<String>,
webhook_url: Option<String>,
job_tags: Option<HashMap<String,String>>)
job_tags: Option<HashMap<String, String>>)
-> i32 {
parse_file_and_execute_with_strategy(factfile,
env,
Expand All @@ -358,7 +358,7 @@ fn parse_file_and_execute_with_strategy<F>(factfile: &str,
strategy: F,
override_result_map: OverrideResultMappings,
webhook_url: Option<String>,
job_tags: Option<HashMap<String,String>>)
job_tags: Option<HashMap<String, String>>)
-> i32
where F: Fn(&str, &mut Command) -> RunResult + Send + Sync + 'static + Copy
{
Expand Down Expand Up @@ -494,12 +494,23 @@ fn parse_file_and_execute_with_strategy<F>(factfile: &str,
}

fn write_to_file(filename: &str, contents: &str, overwrite: bool) -> Result<(), String> {
let mut f = match OpenOptions::new()
.write(true)
.create_new(!overwrite)
.open(filename) {
Ok(f) => f,
Err(io) => return Err(format!("couldn't create file '{}' ({})", filename, io)),
let mut f = if overwrite {
match OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(filename) {
Ok(f) => f,
Err(io) => return Err(format!("couldn't create file '{}' ({})", filename, io)),
}
} else {
match OpenOptions::new()
.write(true)
.create_new(true)
.open(filename) {
Ok(f) => f,
Err(io) => return Err(format!("couldn't create file '{}' ({})", filename, io)),
}
};

match f.write_all(contents.as_bytes()) {
Expand All @@ -519,38 +530,37 @@ fn is_valid_url(url: &str) -> Result<(), String> {
}
}

fn get_constraint_map(constraints: &Vec<String>) -> HashMap<String,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(())
return Ok(());
}

let os_hostname = try!(gethostname_safe().map_err(|e| e.to_string()));

if host == os_hostname {
return Ok(())
if host == os_hostname {
return Ok(());
}

let external_addrs = try!(get_external_addrs().map_err(|e| e.to_string()));
let host_addrs = try!(dns_lookup::lookup_host(&host).map_err(
|_| "could not find any IPv4 addresses for the supplied hostname"
));
let host_addrs = try!(dns_lookup::lookup_host(&host)
.map_err(|_| "could not find any IPv4 addresses for the supplied hostname"));

for host_addr in host_addrs {
if let Ok(good_host_addr) = host_addr {
if external_addrs.iter().any(|external_addr| external_addr.ip() == good_host_addr) {
return Ok(())
return Ok(());
}
}
}

Err("failed to match any of the interface addresses to the found host addresses".into())
}

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

Expand All @@ -560,9 +570,7 @@ fn gethostname_safe() -> Result<String, String> {

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;
let err = unsafe { gethostname(ptr as *mut libc::c_char, len as libc::size_t) } as libc::c_int;

match err {
0 => {
Expand All @@ -578,9 +586,10 @@ fn gethostname_safe() -> Result<String, String> {
}
unsafe { buf.set_len(_real_len) }
Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
},
}
_ => {
Err("could not get hostname from system; cannot compare against supplied hostname".into())
Err("could not get hostname from system; cannot compare against supplied hostname"
.into())
}
}
}
Expand All @@ -599,26 +608,28 @@ fn get_external_addrs() -> Result<Vec<net::SocketAddr>, String> {
}

if external_addrs.len() == 0 {
Err("could not find any non-loopback IPv4 addresses in the network interfaces; do you have a working network interface card?".into())
Err("could not find any non-loopback IPv4 addresses in the network interfaces; do you \
have a working network interface card?"
.into())
} else {
Ok(external_addrs)
}
}

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

for arg in args.iter() {
let split = arg.split(",").collect::<Vec<&str>>();
if split.len() >= 2 && split[0].trim().is_empty() == false {
let key = split[0].trim().to_string();
let value = split[1..].join("").trim().to_string();
arg_map.insert(key, value);
let key = split[0].trim().to_string();
let value = split[1..].join("").trim().to_string();
arg_map.insert(key, value);
} else if split.len() == 1 && split[0].trim().is_empty() == false {
let key = split[0].trim().to_string();
let value = "".to_string();
arg_map.insert(key,value);
}
let key = split[0].trim().to_string();
let value = "".to_string();
arg_map.insert(key, value);
}
}

arg_map
Expand Down Expand Up @@ -744,15 +755,16 @@ fn factotum() -> i32 {
if let Some(host_value) = c_map.get(CONSTRAINT_HOST) {
if let Err(msg) = is_valid_host(host_value) {
println!("{}",
format!("Warn: the specifed host constraint \"{}\" did not match, no tasks have been executed. Reason: {}",
format!("Warn: the specifed host constraint \"{}\" did not match, \
no tasks have been executed. Reason: {}",
host_value,
msg)
.yellow());
return PROC_SUCCESS;
}
}
}

if !args.flag_dry_run {
parse_file_and_execute(&args.arg_factfile,
args.flag_env,
Expand Down Expand Up @@ -821,7 +833,11 @@ fn test_is_valid_url() {

match is_valid_url("potato.com/") {
Ok(_) => panic!("no http/s?"),
Err(msg) => assert_eq!(msg, "URL must begin with 'http://' or 'https://' to be used with Factotum webhooks") // this is good
Err(msg) => {
assert_eq!(msg,
"URL must begin with 'http://' or 'https://' to be used with Factotum \
webhooks")
} // this is good
}
}

Expand Down Expand Up @@ -853,6 +869,11 @@ fn test_write_to_file() {
assert_eq!(contents, "helloworld all");

assert!(fs::remove_file(test_path).is_ok());

// check that overwrite will also write a new file (https://github.com/snowplow/factotum/issues/97)

assert!(write_to_file(test_path, "overwrite test", true).is_ok());
assert!(fs::remove_file(test_path).is_ok());
}

#[test]
Expand Down Expand Up @@ -1292,7 +1313,8 @@ fn test_is_valid_host() {
is_valid_host("*").expect("must be Ok() for wildcard");

// Test each external addr is_valid_host
let external_addrs = get_external_addrs().expect("get_external_addrs() must return a Ok(Vec<net::SocketAddr>) that is non-empty");
let external_addrs = get_external_addrs()
.expect("get_external_addrs() must return a Ok(Vec<net::SocketAddr>) that is non-empty");
for external_addr in external_addrs {
let ip_str = external_addr.ip().to_string();
is_valid_host(&ip_str).expect(&format!("must be Ok() for IP {}", &ip_str));
Expand Down

0 comments on commit 680e90d

Please sign in to comment.