Skip to content

Commit

Permalink
Some handlebars templates can now be rendered
Browse files Browse the repository at this point in the history
  • Loading branch information
tailhook committed Sep 29, 2015
1 parent 9ed7421 commit 59955fb
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 43 deletions.
12 changes: 7 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Options {
config_dir: PathBuf,
dry_run: bool,
print_configs: bool,
hostname: String,
}


Expand All @@ -34,12 +35,16 @@ fn main() {
config_dir: PathBuf::from("/etc/verwalter"),
dry_run: false,
print_configs: false,
hostname: "localhost".to_string(),
};
{
let mut ap = ArgumentParser::new();
ap.refer(&mut options.config_dir)
.add_option(&["-D", "--config-dir"], Parse,
"Directory of configuration files");
ap.refer(&mut options.hostname)
.add_option(&["--hostname"], Parse,
"Hostname of current server");
ap.refer(&mut options.dry_run)
.add_option(&["-n", "--dry-run"], StoreTrue, "
Just try to render configs, and don't run anything real.
Expand Down Expand Up @@ -83,16 +88,13 @@ fn main() {
}
};
debug!("Got initial scheduling of {}", scheduler_result);
/*
let apply_task = match render::render_all(&configs.renderers,
scheduler_result, options.print_configs)
let apply_task = match render::render_all(&config,
scheduler_result, options.hostname, options.print_configs)
{
Ok(res) => res,
Err(e) => {
error!("Initial configuration render failed: {}", e);
exit(5);
}
};
debug!("Rendered config, got {} tasks to apply", apply_task.len());
*/
}
151 changes: 129 additions & 22 deletions src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::io;
use std::io::SeekFrom::{Current, Start};
use std::io::{copy, stdout, Seek};
use std::path::PathBuf;
use std::io::Write;

use tempfile::NamedTempFile;
use handlebars::Handlebars;
use rustc_serialize::json::Json;
use handlebars::{Handlebars, RenderError};
use rustc_serialize::json::{Json, ToJson};

use config::{Config, Version};


pub struct RenderSet {
Expand Down Expand Up @@ -39,31 +39,138 @@ quick_error! {
Io(err: io::Error) {
from() cause(err)
display("I/O error: {}", err)
description("I/O error")
}
SchedulerData(msg: &'static str) {
display("Bad scheduler data {}", msg)
description("bad scheduler data")
}
NoTemplates(role: String, version: String) {
display("No templates for role {:?}, template version {:?}",
role, version)
description("No templates for role (and version)")
}
BadTemplates(role: String, version: String, errors: Vec<String>) {
display("Bad templates for role {:?}, template version {:?},\
errors: {:?}",
role, version, errors)
description("couldn't parse this version of templates")
}
Render(err: RenderError, role: String, version: String, file: String,
data: Json) {
display("Error rendering template of {:?} ver {:?} file {:?}, \
data: {:?} -- {}", role, version, file, data, err)
description("template rendering error")
}
TemplateNotFound(path: PathBuf) {
display("Can't find template: {:?}", path)
RoleMeta(role: String, msg: &'static str) {
display("Metadata for role {:?} {}", role, msg)
description("bad role meta data")
}
NodeRole(role: String, node: String, msg: &'static str) {
display("Metadata for role {:?} on this node {}", role, msg)
description("bad role meta data on this node")
}
NodeNotFound(node: String) {
display("node {:?} not found in scheduler metadata", node)
description("node not found in scheduler metadata")
}
}
}

pub fn render_all<'x>(set: &'x RenderSet, data: Json, print: bool)
-> Result<Vec<(NamedTempFile, &'x Command)>, Error>
pub fn render_all<'x>(cfg: &'x Config, data: Json,
hostname: String, print: bool)
-> Result<Vec<(NamedTempFile, Command)>, Error>
{
let mut result = Vec::new();
for render in &set.items {
let mut tmpfile = try!(NamedTempFile::new());
debug!("Rendered {:?} into {} bytes at {:?}",
&render.source,
tmpfile.seek(Current(0)).unwrap(), tmpfile.path());
if print {
println!("----- [ {:?} -> {:?} ] -----",
render.source, tmpfile.path());
tmpfile.seek(Start(0)).unwrap();
try!(copy(&mut tmpfile, &mut stdout()));
println!("----- End of [ {:?} -> {:?} ] -----",
render.source, tmpfile.path());
let meta = data.as_object()
.and_then(|x| x.get("role_metadata"))
.and_then(|y| y.as_object());
let meta = match meta {
Some(x) => x,
None => {
return Err(Error::SchedulerData(
r#"No key "role_metadata" or is not a dict"#));
}
};
let node = data.as_object()
.and_then(|x| x.get("nodes"))
.and_then(|y| y.as_object())
.and_then(|x| x.get(&hostname))
.and_then(|y| y.as_object());
let node = match node {
Some(x) => x,
None => {
return Err(Error::NodeNotFound(hostname.clone()));
}
};
for (name, role) in &cfg.roles {
let role_meta = match meta.get(name) {
Some(&Json::Object(ref ob)) => ob,
Some(_) => {
return Err(Error::RoleMeta(name.clone(), "not an object"));
}
None => {
debug!("Skipping role {:?}", name);
continue;
}
};
let tpl_ver = match role_meta.get("templates") {
Some(&Json::String(ref val)) => Version(val.to_string()),
Some(_) => {
return Err(Error::RoleMeta(name.clone(),
r#""templates" is not a string"#));
}
None => {
return Err(Error::RoleMeta(name.clone(),
r#"no "templates" is role metadata"#));
}
};
let rnd = match role.renderers.get(&tpl_ver) {
Some(&Ok(ref rnd)) => rnd,
Some(&Err(ref e)) => {
return Err(Error::BadTemplates(name.clone(), tpl_ver.0.clone(),
e.errors.iter().map(|x| x.to_string()).collect()));
}
None => {
return Err(Error::NoTemplates(name.clone(), tpl_ver.0.clone()));
}
};
let node_role = match node.get(name) {
Some(&Json::Object(ref ob)) => ob,
Some(_) => {
return Err(Error::NodeRole(name.clone(), hostname.clone(),
"not an object"));
}
None => {
debug!("No role {:?} at node {:?}", name, hostname);
continue;
}
};
for render in &rnd.items {
let mut tmpfile = try!(NamedTempFile::new());
let data = Json::Object(vec![
("verwalter_version".to_string(),
concat!("v", env!("CARGO_PKG_VERSION")).to_json()),
("role".to_string(), Json::Object(role_meta.clone())),
("node".to_string(), Json::Object(node_role.clone())),
].into_iter().collect());

let output = try!(rnd.handlebars.render(&render.source, &data)
.map_err(|e| Error::Render(e,
name.clone(), tpl_ver.0.clone(), render.source.clone(),
data)));
try!(tmpfile.write_all(output.as_bytes()));
debug!("Rendered {:?} into {} bytes at {:?}",
&render.source, output.as_bytes().len(), tmpfile.path());
if print {
println!("----- [ {:?} -> {:?} ] -----",
render.source, tmpfile.path());
print!("{}", output);
println!("----- End of [ {:?} -> {:?} ] -----",
render.source, tmpfile.path());
}
result.push((tmpfile, render.apply.clone()));
}
result.push((tmpfile, &render.apply));
}
Ok(result)
}
2 changes: 1 addition & 1 deletion src/scheduler/lua_json.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::i64;

use lua::{ToLua, State};
use lua::{State};
use rustc_serialize::json::Json;


Expand Down
26 changes: 11 additions & 15 deletions src/scheduler/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use std::io;
use std::io::Read;
use std::fs::File;
use std::path::{Path, PathBuf};

use rustc_serialize::json::{Json, BuilderError};
use rustc_serialize::json::{Json};
use lua::{State, ThreadStatus, Type};
use config::Config;

Expand All @@ -17,12 +14,8 @@ pub struct Scheduler {
quick_error! {
#[derive(Debug)]
pub enum ReadError {
Read(err: io::Error, path: PathBuf) {
display("error reading lua script {:?}: {:?}", path, err)
description("error reading lua script")
}
Lua(err: ThreadStatus, path: PathBuf) {
display("error parsing lua script {:?}: {:?}", path, err)
Lua(err: ThreadStatus, msg: String, path: PathBuf) {
display("error parsing lua script {:?}: {:?}: {}", path, err, msg)
description("error parsing lua script")
}
UnexpectedYield(path: PathBuf) {
Expand Down Expand Up @@ -63,13 +56,16 @@ pub fn read(base_dir: &Path) -> Result<Scheduler, ReadError> {
// should have more control over which libraries to use
lua.open_libs();

let path = &base_dir.join("scheduler/main.lua");
let path = &base_dir.join("scheduler/v1/main.lua");
{
try!(match lua.do_file(&path.to_str().unwrap_or("undecodable")) {
ThreadStatus::Ok => Ok(()),
ThreadStatus::Yield
=> Err(ReadError::UnexpectedYield(path.clone())),
x => Err(ReadError::Lua(x, path.clone())),
x => {
let e = lua.to_str(-1).unwrap_or("undefined").to_string();
Err(ReadError::Lua(x, e, path.clone()))
}
});
}
Ok(Scheduler {
Expand All @@ -95,9 +91,9 @@ impl Scheduler {
self.lua.to_str(-1).unwrap_or("undefined").to_string()))
}
}
match self.lua.to_type() {
Some(x) => Ok(Json::String(x)),
None => return Err(Error::Conversion),
match self.lua.to_type::<String>() {
Some(ref x) => Json::from_str(x).map_err(|_| Error::Conversion),
None => Err(Error::Conversion),
}
}
}
3 changes: 3 additions & 0 deletions src/scheduler/result.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
struct Schedule {
nodes: HashMap<String, Node>,
}

0 comments on commit 59955fb

Please sign in to comment.