extern crate docopt;
extern crate glob;
extern crate libedgegrid;
extern crate libmultilog;
#[macro_use]
extern crate log;
extern crate regex;
extern crate rustc_serialize;
extern crate time;
use docopt::Docopt;
use libedgegrid::auth;
use libmultilog::multi::{MultiLogger, init_multi_logger};
use log::{LogLevel, LogRecord};
use log::LogLevelFilter::*;
use regex::Regex;
use self::InitError::*;
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::{self, BufReader, BufWriter, ErrorKind, Read, Write};
use std::path::Path;
use std::process;
mod api;
mod version;
#[cfg_attr(rustfmt, rustfmt_skip)]
static USAGE: &'static str = "egc - Run the Akamai EdgeGrid Client
Usage:
egc [options] help [<helpcmd>]
egc [options] (alert | ccu | dt | events | lds) <akargs>...
egc -h
egc -V [-v]
Options:
-h --help Show this message.
-V --version Show version information.
-v --verbose Turn on verbose output.
-f CPATH --file=CPATH Configuration file path.
-q --quiet Disable stdout logging.
--logfile=FPATH Enable file logging at path.
-c --cpcode Enable cpcode purge request.
-i --byid Lookup the alert by id.
-t MS --timeout=MS Set the curl timeout for the request in milliseconds.
Examples:
N/A";
#[derive(Debug, RustcDecodable)]
pub struct Args {
arg_akargs: Vec<String>,
arg_helpcmd: String,
cmd_alert: bool,
cmd_ccu: bool,
cmd_dt: bool,
cmd_events: bool,
cmd_help: bool,
cmd_lds: bool,
flag_byid: bool,
flag_cpcode: bool,
flag_file: String,
flag_help: bool,
flag_logfile: String,
flag_quiet: bool,
flag_timeout: usize,
flag_verbose: bool,
flag_version: bool,
}
pub type AuthsHashMap = HashMap<String, auth::EdgeGridAuth>;
#[derive(Debug)]
enum InitError {
IO(io::Error),
GLOB(glob::PatternError),
LOG(log::SetLoggerError),
RE(regex::Error),
}
impl From<io::Error> for InitError {
fn from(err: io::Error) -> InitError {
IO(err)
}
}
impl From<glob::PatternError> for InitError {
fn from(err: glob::PatternError) -> InitError {
GLOB(err)
}
}
impl From<log::SetLoggerError> for InitError {
fn from(err: log::SetLoggerError) -> InitError {
LOG(err)
}
}
impl From<regex::Error> for InitError {
fn from(err: regex::Error) -> InitError {
RE(err)
}
}
fn stdoutfn(record: &LogRecord) {
println!("{}", record.args());
}
fn fileoutfn(record: &LogRecord, w: &mut BufWriter<File>) {
let now = time::now();
w.write_fmt(format_args!("{} {:5} {:4} -- {}: {}\n",
now.rfc3339(),
record.level(),
record.location().line(),
record.location().module_path(),
record.args()))
.and(w.flush())
.unwrap();
}
fn init(stdout: bool, file_path: Option<&Path>, verbose: bool) -> Result<(), InitError> {
let mut ml: MultiLogger = Default::default();
if stdout {
ml.enable_stdout(stdoutfn);
}
if file_path.is_some() {
let ref mut logpath = file_path.unwrap();
let parent = try!(logpath.parent().ok_or_else(|| {
io::Error::new(ErrorKind::Other, "Unable to determine parent!")
}));
match fs::metadata(parent) {
Ok(_) => {}
Err(_) => {
try!(fs::create_dir_all(parent));
}
}
ml.enable_file(fileoutfn, logpath.to_path_buf());
let r = try!(Regex::new("^mio"));
ml.add_file_filter(LogLevel::Debug, r);
}
try!(init_multi_logger(if verbose {
Debug
} else {
Info
},
ml));
Ok(())
}
fn parse_client_file(path: &Path) -> Result<auth::EdgeGridAuth, io::Error> {
let client_file = try!(File::open(path));
let mut reader = BufReader::new(client_file);
let mut out = String::new();
try!(reader.read_to_string(&mut out));
let non_empty_lines: Vec<&str> = out.lines()
.filter_map(|s| {
let t = s.trim();
if t.is_empty() {
None
} else {
Some(t)
}
})
.collect();
let name_split: Vec<&str> = non_empty_lines[1].split(": ").collect();
let name = name_split[1];
let url_split: Vec<&str> = non_empty_lines[2].split(": ").collect();
let baseurl = url_split[1].trim_right_matches('/');
let access_token = non_empty_lines[4];
let client_split: Vec<&str> = non_empty_lines[6].split(" ").collect();
let client_filtered: Vec<&str> = client_split.into_iter()
.filter(|s| !s.is_empty())
.collect();
let client_token = client_filtered[2];
let client_secret = client_filtered[4];
let mut egr = auth::EdgeGridAuth::new(name, baseurl);
egr.set_access_token(access_token);
egr.set_client_token(client_token);
egr.set_client_secret(client_secret);
Ok(egr)
}
type ParseResult = Result<HashMap<String, auth::EdgeGridAuth>, InitError>;
fn parse_client_files() -> ParseResult {
let mut clients = HashMap::new();
let mut egcdir = try!(env::home_dir().ok_or_else(|| {
io::Error::new(ErrorKind::Other, "Unable to determine home directory!")
}));
egcdir.push(".egc");
let mut pattern = String::from(egcdir.to_str().unwrap());
pattern.push_str("/client-*.txt");
let glob = try!(glob::glob(&pattern[..]));
for path in glob.filter_map(Result::ok) {
debug!("Parsing {}", path.display());
let egr = try!(parse_client_file(&path));
clients.insert(String::from(egr.name()), egr);
}
Ok(clients)
}
fn exit(msg: String, code: u8) -> ! {
if !msg.is_empty() {
if code == 0 {
info!("{}", msg);
} else {
error!("{}", msg);
}
}
process::exit(code as i32)
}
fn get_auth<'a>(name: &str,
timeout: usize,
auths: &'a mut HashMap<String, auth::EdgeGridAuth>)
-> &'a mut auth::EdgeGridAuth {
let mut egr = match auths.get_mut(name) {
Some(e) => e,
None => {
::exit(String::from("Unable to get authorization"), 1);
}
};
if timeout > 0 {
egr.set_timeout(timeout);
}
egr
}
fn subcommand(base: &str, args: &Args) -> Vec<String> {
let mut argv = Vec::new();
argv.push(String::from(base));
if args.flag_timeout > 0 {
argv.push(String::from("-t"));
argv.push(format!("{}", args.flag_timeout));
}
if args.flag_cpcode {
argv.push(String::from("-c"));
}
if args.flag_byid {
argv.push(String::from("-i"));
}
argv.append(&mut args.arg_akargs.clone());
argv
}
fn main() {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.decode())
.unwrap_or_else(|e| e.exit());
let fp = if args.flag_file.is_empty() {
None
} else {
Some(Path::new(&args.flag_file[..]))
};
match init(!args.flag_quiet, fp, args.flag_verbose) {
Ok(_) => {}
Err(e) => {
exit(format!("{:?}", e), 1);
}
}
if args.flag_version {
println!("{}", version::version(args.flag_verbose));
exit(String::new(), 0);
} else if args.cmd_help {
if args.arg_helpcmd.is_empty() {
println!("{}", USAGE);
} else {
match &args.arg_helpcmd[..] {
#[cfg(feature="ccu")]
"ccu" => {
println!("{}", api::ccu::usage());
}
#[cfg(feature = "luna")]
"alert" => {
println!("{}", api::luna::alert::usage());
}
#[cfg(feature = "luna")]
"dt" => {
println!("{}", api::luna::dt::usage());
}
_ => {
println!("Unknown command!");
}
}
}
exit(String::new(), 0);
} else {
let mut auths = match parse_client_files() {
Ok(a) => a,
Err(e) => {
exit(format!("{:?}", e), 1);
}
};
match args.cmd_ccu {
#[cfg(feature = "ccu")]
true => api::ccu::parse_args(subcommand("ccu", &args), &mut auths),
#[cfg(not(feature = "ccu"))]
true => {}
false => {}
}
match args.cmd_dt {
#[cfg(feature = "luna")]
true => api::luna::dt::parse_args(subcommand("dt", &args), &mut auths),
#[cfg(not(feature = "luna"))]
true => {}
false => {}
}
match args.cmd_alert {
#[cfg(feature = "luna")]
true => api::luna::alert::parse_args(subcommand("alert", &args), &mut auths),
#[cfg(not(feature = "luna"))]
true => {}
false => {}
}
}
}