use std::collections::HashMap;
use std::env;
#[derive(Debug)]
pub enum Transport {
Tcp,
Udp,
}
impl Transport {
fn to_string(self) -> String {
match self {
Transport::Tcp => "tcp".to_string(),
Transport::Udp => "udp".to_string(),
}
}
}
#[derive(Debug)]
pub struct Context{
pub relation_type: String,
pub relation_id: usize,
pub unit: String,
pub relations: HashMap<String,String>,
}
impl Context{
pub fn new_from_env() -> Context{
let relations: HashMap<String,String> = HashMap::new();
let relation_type = env::var("JUJU_RELATION").unwrap_or("".to_string());
let relation_id_str = env::var("JUJU_RELATION_ID").unwrap_or("".to_string());
let parts: Vec<&str> = relation_id_str.split(":").collect();
let relation_id: usize = parts[1].parse::<usize>().unwrap();
let unit = env::var("JUJU_UNIT_NAME").unwrap_or("".to_string());
Context{
relation_type: relation_type,
relation_id: relation_id,
unit: unit,
relations: relations,
}
}
}
#[derive(Debug)]
pub struct Relation {
pub name: String,
pub id: usize
}
pub struct Hook {
pub name: String,
pub callback: Box<Fn()->Result<(),String>>,
}
pub fn log(msg: &String){
let mut arg_list: Vec<String> = Vec::new();
arg_list.push(msg.clone());
run_command("juju-log", &arg_list, false).is_ok();
}
pub fn reboot()->Result<i32,String>{
let output = try!(run_command_no_args("juju-reboot", true).map_err(|e| e.to_string()));
let status = output.status;
match status.code(){
Some(v) => Ok(v),
None => Err(try!(String::from_utf8(output.stderr).map_err(|e| e.to_string()))),
}
}
pub fn unit_get_private_addr() ->Result<String, String>{
let mut arg_list: Vec<String> = Vec::new();
arg_list.push("private-address".to_string());
let output = try!(run_command("unit-get", &arg_list, false).map_err(|e| e.to_string()));
let private_addr: String = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
return Ok(private_addr.trim().to_string());
}
pub fn unit_get_public_addr() ->Result<String, String>{
let mut arg_list: Vec<String> = Vec::new();
arg_list.push("public-address".to_string());
let output = try!(run_command("unit-get", &arg_list, false).map_err(|e| e.to_string()));
let public_addr = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
return Ok(public_addr.trim().to_string());
}
pub fn config_get(key: &String) ->Result<String, String>{
let mut arg_list: Vec<String> = Vec::new();
arg_list.push(key.clone());
let output = try!(run_command("config-get", &arg_list, false).map_err(|e| e.to_string()));
let value = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
return Ok(value.trim().to_string());
}
pub fn open_port(port: usize, transport: Transport)->Result<i32, String>{
let mut arg_list: Vec<String> = Vec::new();
let port_string = format!("{}/{}", port.to_string(), transport.to_string());
arg_list.push(port_string);
let output = try!(run_command("open-port", &arg_list, false).map_err(|e| e.to_string()));
let status = output.status;
match status.code(){
Some(v) => Ok(v),
None => Err(try!(String::from_utf8(output.stderr).map_err(|e| e.to_string()))),
}
}
pub fn close_port(port: usize, transport: Transport)->Result<i32, String>{
let mut arg_list: Vec<String> = Vec::new();
let port_string = format!("{}/{}", port.to_string() , transport.to_string());
arg_list.push(port_string);
let output = try!(run_command("close-port", &arg_list, false).map_err(|e| e.to_string()));
let status = output.status;
match status.code(){
Some(v) => Ok(v),
None => Err(try!(String::from_utf8(output.stderr).map_err(|e| e.to_string()))),
}
}
pub fn relation_set(key: &str, value: &str)->Result<i32, String>{
let mut arg_list: Vec<String> = Vec::new();
let arg = format!("{}={}", key.clone(), value);
arg_list.push(arg);
let output = try!(run_command("relation-set", &arg_list, false).map_err(|e| e.to_string()));
let status = output.status;
match status.code(){
Some(v) => Ok(v),
None => Err(try!(String::from_utf8(output.stderr).map_err(|e| e.to_string()))),
}
}
pub fn relation_get(key: &String) -> Result<String,String>{
let mut arg_list: Vec<String> = Vec::new();
arg_list.push(key.clone());
let output = try!(run_command("relation-get", &arg_list, false).map_err(|e| e.to_string()));
let value = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
return Ok(value);
}
pub fn relation_get_by_unit(key: &String, unit: &Relation) -> Result<String,String>{
let mut arg_list: Vec<String> = Vec::new();
arg_list.push(key.clone());
arg_list.push(format!("{}/{}", unit.name , unit.id.to_string()));
let output = try!(run_command("relation-get", &arg_list, false).map_err(|e| e.to_string()));
let relation = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
return Ok(relation);
}
pub fn relation_list() ->Result<Vec<Relation>, String>{
let mut related_units: Vec<Relation> = Vec::new();
let output = try!(run_command_no_args("relation-list", false).map_err(|e| e.to_string()));
let output_str = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
log(&format!("relation-list output: {}", output_str));
for line in output_str.lines(){
let v: Vec<&str> = line.split('/').collect();
let id: usize = try!(v[1].parse::<usize>().map_err(|e| e.to_string()));
let r: Relation = Relation{
name: v[0].to_string(),
id: id,
};
related_units.push(r);
}
return Ok(related_units);
}
pub fn relation_ids() ->Result<Vec<Relation>, String>{
let mut related_units: Vec<Relation> = Vec::new();
let output = try!(run_command_no_args("relation-ids", false).map_err(|e| e.to_string()));
let output_str: String = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
log(&format!("relation-ids output: {}", output_str));
for line in output_str.lines(){
let v: Vec<&str> = line.split(':').collect();
let id: usize = try!(v[1].parse::<usize>().map_err(|e| e.to_string()));
let r: Relation = Relation{
name: v[0].to_string(),
id: id,
};
related_units.push(r);
}
return Ok(related_units);
}
pub fn storage_get_location() ->Result<String, String>{
let mut arg_list: Vec<String> = Vec::new();
arg_list.push("location".to_string());
let output = try!(run_command("storage-get", &arg_list, false).map_err(|e| e.to_string()));
return Ok(try!(String::from_utf8(output.stdout).map_err(|e| e.to_string())));
}
pub fn process_hooks(args: Vec<String>, registry: Vec<Hook>)->Result<(),String>{
let path = std::path::Path::new(args[0].trim());
let filename = match path.file_name(){
Some(filename) => filename,
None => {
return Err(format!("Unable to parse filename from {:?}", path));
},
};
let match_str = match filename.to_str(){
Some(filename) => filename,
None => {
return Err(format!("Failed to transform filename into string {:?}. Bad symlink name perhaps? Bailing", filename));
},
};
for hook in registry {
if hook.name == match_str{
return (*hook.callback)();
}
}
return Err(format!("Warning: Failed to match callback for {}", match_str));
}
pub fn is_leader()->Result<bool, String>{
let output = try!(run_command_no_args("is-leader", false).map_err(|e| e.to_string()));
let output_str: String = try!(String::from_utf8(output.stdout).map_err(|e| e.to_string()));
match output_str.trim().as_ref() {
"True" => Ok(true),
"False" => Ok(false),
_ => Ok(false),
}
}
fn run_command_no_args(command: &str, as_root: bool)-> Result<std::process::Output, String>{
if as_root{
let mut cmd = std::process::Command::new("sudo");
let output = try!(cmd.output().map_err(|e| e.to_string()));
return Ok(output);
}else{
let mut cmd = std::process::Command::new(command);
let output = try!(cmd.output().map_err(|e| e.to_string()));
return Ok(output);
}
}
fn run_command(command: &str, arg_list: &Vec<String>, as_root: bool) -> Result<std::process::Output, String>{
if as_root{
let mut cmd = std::process::Command::new("sudo");
cmd.arg(command);
for arg in arg_list{
cmd.arg(&arg);
}
let output = try!(cmd.output().map_err(|e| e.to_string()));
return Ok(output);
}else{
let mut cmd = std::process::Command::new(command);
for arg in arg_list{
cmd.arg(&arg);
}
let output = try!(cmd.output().map_err(|e| e.to_string()));
return Ok(output);
}
}