Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor Sidecar into an actor #71

Merged
merged 1 commit into from
Nov 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/bldr/command/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,3 @@ pub fn package(config: &Config) -> BldrResult<()> {
}
Ok(())
}

9 changes: 7 additions & 2 deletions src/bldr/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ impl Package {
}
}

/// Get status of running package.
pub fn status(&self) -> BldrResult<String> {
self.signal(Signal::Status)
}

pub fn exposes(&self) -> Vec<String> {
match fs::metadata(self.join_path("EXPOSES")) {
Ok(_) => {
Expand Down Expand Up @@ -419,9 +424,9 @@ impl Package {
}
}

pub fn health_check(&self) -> BldrResult<CheckResult> {
pub fn health_check(&self, config: &ServiceConfig) -> BldrResult<CheckResult> {
if let Some(hook) = self.hooks().health_check_hook {
match hook.run(None) {
match hook.run(Some(config)) {
Ok(output) => Ok(health_check::CheckResult::ok(output)),
Err(BldrError::HookFailed(_, 1, output)) => Ok(health_check::CheckResult::warning(output)),
Err(BldrError::HookFailed(_, 2, output)) => Ok(health_check::CheckResult::critical(output)),
Expand Down
136 changes: 86 additions & 50 deletions src/bldr/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,84 @@
use iron::prelude::*;
use iron::status;
use router::Router;
use std::sync::Arc;
use std::thread;
use std::sync::{Arc, RwLock};

use error::{BldrError, BldrResult};
use wonder;
use wonder::actor::{GenServer, InitResult, HandleResult, StopReason, ActorSender};

use pkg::{Package, Signal};
use error::BldrError;
use health_check;
use pkg::Package;
use service_config::ServiceConfig;

/// The sidecar state
struct Sidecar {
const GET_HEALTH: &'static str = "/health";
const GET_CONFIG: &'static str = "/config";
const GET_STATUS: &'static str = "/status";
const LISTEN_ADDR: &'static str = "0.0.0.0:9631";

pub type SidecarActor = wonder::actor::Actor<SidecarMessage>;

pub struct Sidecar;

pub struct SidecarState {
/// The package this sidecar is helping out
pub package: Package,
pub package: Arc<RwLock<Package>>,
/// The configuration of the supervised service
pub config: Arc<RwLock<ServiceConfig>>,
}

#[derive(Debug)]
pub enum SidecarMessage {
Ok,
Stop,
}

impl SidecarState {
pub fn new(package: Arc<RwLock<Package>>, config: Arc<RwLock<ServiceConfig>>) -> Self {
SidecarState {
package: package,
config: config,
}
}
}

impl Sidecar {
/// Returns a new sidecar.
///
/// # Failures
///
/// * If the package cannot be found
fn new(pkg: &str) -> BldrResult<Arc<Sidecar>> {
let package = try!(Package::latest(pkg, None));
Ok(Arc::new(Sidecar{package: package}))
/// Start the sidecar.
pub fn start(package: Arc<RwLock<Package>>, config: Arc<RwLock<ServiceConfig>>) -> SidecarActor {
let state = SidecarState::new(package, config);
wonder::actor::Builder::new(Sidecar).name("sidecar".to_string()).start(state).unwrap()
}
}

impl GenServer for Sidecar {
type T = SidecarMessage;
type S = SidecarState;
type E = BldrError;

fn init(&self, _tx: &ActorSender<Self::T>, _state: &mut Self::S) -> InitResult<Self::E> {
Ok(Some(0))
}

fn handle_timeout(
&self,
_tx: &ActorSender<Self::T>,
_me: &ActorSender<Self::T>,
state: &mut Self::S
) -> HandleResult<Self::T> {
let mut router = Router::new();
let package_1 = state.package.clone();
let package_2 = state.package.clone();
let package_3 = state.package.clone();
let config_1 = state.config.clone();

router.get(GET_CONFIG, move |r: &mut Request| config(&package_1, r));
router.get(GET_STATUS, move |r: &mut Request| status(&package_2, r));
router.get(GET_HEALTH, move |r: &mut Request| health(&package_3, &config_1, r));

match Iron::new(router).http(LISTEN_ADDR) {
Ok(_) => HandleResult::NoReply(None),
Err(_) => HandleResult::Stop(StopReason::Fatal("couldn't start router".to_string()), None),
}
}
}

Expand All @@ -60,8 +115,9 @@ impl Sidecar {
/// # Failures
///
/// * Fails if the configuration cannot be found.
fn config(sidecar: &Sidecar, _req: &mut Request) -> IronResult<Response> {
let last_config = try!(sidecar.package.last_config());
fn config(lock: &Arc<RwLock<Package>>, _req: &mut Request) -> IronResult<Response> {
let package = lock.read().unwrap();
let last_config = try!(package.last_config());
Ok(Response::with((status::Ok, last_config)))
}

Expand All @@ -72,8 +128,9 @@ fn config(sidecar: &Sidecar, _req: &mut Request) -> IronResult<Response> {
/// # Failures
///
/// * Fails if the supervisor cannot return the status.
fn status(sidecar: &Sidecar, _req: &mut Request) -> IronResult<Response> {
let output = try!(sidecar.package.signal(Signal::Status));
fn status(lock: &Arc<RwLock<Package>>, _req: &mut Request) -> IronResult<Response> {
let package = lock.read().unwrap();
let output = try!(package.status());
Ok(Response::with((status::Ok, output)))
}

Expand All @@ -85,8 +142,16 @@ fn status(sidecar: &Sidecar, _req: &mut Request) -> IronResult<Response> {
/// # Failures
///
/// * If the health_check cannot be run.
fn health(sidecar: &Sidecar, _req: &mut Request) -> IronResult<Response> {
let result = try!(sidecar.package.health_check());
fn health(
package_lock: &Arc<RwLock<Package>>,
config_lock: &Arc<RwLock<ServiceConfig>>,
_req: &mut Request
) -> IronResult<Response> {
let result = {
let package = package_lock.read().unwrap();
let config = config_lock.read().unwrap();
try!(package.health_check(&config))
};

match result.status {
health_check::Status::Ok | health_check::Status::Warning => {
Expand All @@ -101,38 +166,9 @@ fn health(sidecar: &Sidecar, _req: &mut Request) -> IronResult<Response> {
}
}

/// Start the sidecar.
///
/// # Failures
///
/// * If the thread cannot be spawned
pub fn run(pkg: &str) -> BldrResult<()> {
let pkg_name = String::from(pkg);
try!(thread::Builder::new().name(String::from("sidecar")).spawn(move || -> BldrResult<()> {
// The sidecar is in an Arc. The clones are
// creating instances to share, and when they all go away, we'll
// reap the instance. Turns out they won't really ever go away,
// but you do what you need to :)
let sidecar = try!(Sidecar::new(&pkg_name));
let sidecar2 = sidecar.clone();
let sidecar3 = sidecar.clone();

let mut router = Router::new();

router.get("/config", move |r: &mut Request| config(&sidecar, r));
router.get("/status", move |r: &mut Request| status(&sidecar2, r));
router.get("/health", move |r: &mut Request| health(&sidecar3, r));

Iron::new(router).http("0.0.0.0:9631").unwrap();
Ok(())
}));
Ok(())
}

/// Translates BldrErrors into IronErrors
impl From<BldrError> for IronError {
fn from(err: BldrError) -> IronError {
IronError{error: Box::new(err), response: Response::with((status::InternalServerError, "Internal bldr error"))}
}
}

Loading