Skip to content

Commit

Permalink
Merge pull request #316 from chef/dp_replace_runit
Browse files Browse the repository at this point in the history
Merged change 124a8648-9a8a-48fe-93a4-325ee28f8a5c

From review branch dp_replace_runit into master

Signed-off-by: dparfitt <[email protected]>
  • Loading branch information
chef-delivery committed Mar 25, 2016
2 parents 29eb8bf + 7784be6 commit 021bec8
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 248 deletions.
10 changes: 7 additions & 3 deletions components/bldr/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub enum ErrorKind {
PackageIdentMismatch(String, String),
RemotePackageNotFound(package::PackageIdent),
MustacheMergeOnlyMaps,
SupervisorSignalFailed,
SignalFailed,
StringFromUtf8Error(string::FromUtf8Error),
StrFromUtf8Error(str::Utf8Error),
SupervisorDied,
Expand All @@ -145,6 +145,7 @@ pub enum ErrorKind {
JsonEncode(json::EncoderError),
JsonDecode(json::DecoderError),
InitialPeers,
InvalidPidFile,
}

/// Our result type alias, for easy coding.
Expand Down Expand Up @@ -217,7 +218,7 @@ impl fmt::Display for BldrError {
}
}
ErrorKind::MustacheMergeOnlyMaps => format!("Can only merge two Mustache::Data::Maps"),
ErrorKind::SupervisorSignalFailed => format!("Failed to send a signal to the process supervisor"),
ErrorKind::SignalFailed => format!("Failed to send a signal to the child process"),
ErrorKind::StringFromUtf8Error(ref e) => format!("{}", e),
ErrorKind::StrFromUtf8Error(ref e) => format!("{}", e),
ErrorKind::SupervisorDied => format!("The supervisor died"),
Expand Down Expand Up @@ -252,6 +253,8 @@ impl fmt::Display for BldrError {
ErrorKind::JsonEncode(ref e) => format!("JSON encoding error: {}", e),
ErrorKind::JsonDecode(ref e) => format!("JSON decoding error: {}", e),
ErrorKind::InitialPeers => format!("Failed to contact initial peers"),
ErrorKind::InvalidPidFile => format!("Invalid child process PID file"),

};
let cstring = Red.bold().paint(content).to_string();
let mut so = StructuredOutput::new("bldr",
Expand Down Expand Up @@ -300,7 +303,7 @@ impl Error for BldrError {
ErrorKind::PackageIdentMismatch(_, _) => "Expected a package identity but received another",
ErrorKind::RemotePackageNotFound(_) => "Cannot find a package in any sources",
ErrorKind::MustacheMergeOnlyMaps => "Can only merge two Mustache::Data::Maps",
ErrorKind::SupervisorSignalFailed => "Failed to send a signal to the process supervisor",
ErrorKind::SignalFailed => "Failed to send a signal to the child process",
ErrorKind::StringFromUtf8Error(_) => "Failed to convert a string from a Vec<u8> as UTF-8",
ErrorKind::StrFromUtf8Error(_) => "Failed to convert a str from a &[u8] as UTF-8",
ErrorKind::SupervisorDied => "The supervisor died",
Expand All @@ -324,6 +327,7 @@ impl Error for BldrError {
ErrorKind::JsonEncode(_) => "JSON encoding error",
ErrorKind::JsonDecode(_) => "JSON decoding error: {:?}",
ErrorKind::InitialPeers => "Failed to contact initial peers",
ErrorKind::InvalidPidFile => "Invalid child process PID file",
}
}
}
Expand Down
204 changes: 106 additions & 98 deletions components/bldr/src/package/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::string::ToString;
use std::io::prelude::*;
use std::process::Command;
use std::env;
use std::os::unix::fs::MetadataExt;

use time;
use time::Timespec;

use core::fs::{PACKAGE_CACHE, PACKAGE_HOME, SERVICE_HOME};
use core::package::{version_sort, MetaFile, PackageIdent};
Expand All @@ -36,34 +39,7 @@ const INIT_FILENAME: &'static str = "init";
const HEALTHCHECK_FILENAME: &'static str = "health_check";
const RECONFIGURE_FILENAME: &'static str = "reconfigure";
const RUN_FILENAME: &'static str = "run";

pub enum Signal {
Status,
Up,
Down,
Once,
Pause,
Cont,
Hup,
Alarm,
Interrupt,
Quit,
One,
Two,
Term,
Kill,
Exit,
Start,
Stop,
Reload,
Restart,
Shutdown,
ForceStop,
ForceReload,
ForceRestart,
ForceShutdown,
TryRestart,
}
const PIDFILE_NAME: &'static str = "PID";

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Package {
Expand All @@ -75,6 +51,7 @@ pub struct Package {
pub tdeps: Vec<PackageIdent>,
}


impl Package {
pub fn deps<P: AsRef<Path>>(ident: &PackageIdent, home: P) -> BldrResult<Vec<PackageIdent>> {
Self::read_deps(home.as_ref().join(ident.to_string()), MetaFile::Deps)
Expand Down Expand Up @@ -194,67 +171,98 @@ impl Package {
}
}

pub fn signal(&self, signal: Signal) -> BldrResult<String> {
let runit_pkg = try!(Self::load(&PackageIdent::new("chef", "runit", None, None), None));
let signal_arg = match signal {
Signal::Status => "status",
Signal::Up => "up",
Signal::Down => "down",
Signal::Once => "once",
Signal::Pause => "pause",
Signal::Cont => "cont",
Signal::Hup => "hup",
Signal::Alarm => "alarm",
Signal::Interrupt => "interrupt",
Signal::Quit => "quit",
Signal::One => "1",
Signal::Two => "2",
Signal::Term => "term",
Signal::Kill => "kill",
Signal::Exit => "exit",
Signal::Start => "start",
Signal::Stop => "stop",
Signal::Reload => "reload",
Signal::Restart => "restart",
Signal::Shutdown => "shutdown",
Signal::ForceStop => "force-stop",
Signal::ForceReload => "force-reload",
Signal::ForceRestart => "force-restart",
Signal::ForceShutdown => "force-shutdown",
Signal::TryRestart => "try-restart",
};
let output = try!(Command::new(runit_pkg.join_path("bin/sv"))
.arg(signal_arg)
.arg(&format!("{}/{}", SERVICE_HOME, self.name))
.output());
match output.status.success() {
true => {
let stdout = try!(String::from_utf8(output.stdout));
return Ok(stdout);
/// Create a pid file for a package
/// The existence of this file does not guarantee that a
/// process exists at the PID contained within.
pub fn create_pidfile(&self, pid: u32) -> BldrResult<()> {
let service_dir = format!("{}/{}", SERVICE_HOME, self.name);
let pid_file = format!("{}/{}", service_dir, PIDFILE_NAME);
debug!("Creating PID file for child {} -> {}", pid_file, pid);
let mut f = try!(File::create(pid_file));
try!(write!(f, "{}", pid));
Ok(())
}


/// Remove a pidfile for this package if it exists.
/// Do NOT fail if there is an error removing the PIDFILE
pub fn cleanup_pidfile(&self) -> BldrResult<()> {
let service_dir = format!("{}/{}", SERVICE_HOME, self.name);
let pid_file = format!("{}/{}", service_dir, PIDFILE_NAME);
debug!("Attempting to clean up pid file {}", &pid_file);
match fs::remove_file(pid_file) {
Ok(_) => {
debug!("Removed pid file");
}
false => {
match signal {
Signal::ForceShutdown => {
let outstr = try!(String::from_utf8(output.stdout));
return Ok(outstr);
}
_ => {}
Err(e) => {
debug!("Error removing pidfile: {}, continuing", e);
}
};
Ok(())
}

/// attempt to read the pidfile for this package.
/// If the pidfile does not exist, then return None,
/// otherwise, return Some(pid, uptime_seconds).
pub fn read_pidfile(&self) -> BldrResult<Option<(u32, Timespec)>> {
let service_dir = format!("{}/{}", SERVICE_HOME, self.name);
let pid_file = format!("{}/{}", service_dir, PIDFILE_NAME);
debug!("Reading pidfile {}", &pid_file);

// check to see if the file exists
// if it does, we'll return the start_time
let start_time = match fs::metadata(&pid_file) {
Ok(metadata) => {
if metadata.is_file() {
Timespec::new(metadata.ctime(),0 /* nanos */)
} else {
return Ok(None);
}
debug!("Failed to send signal to the process supervisor for {}",
self.name);
let outstr = try!(String::from_utf8(output.stdout));
let errstr = try!(String::from_utf8(output.stderr));
debug!("Supervisor (O): {}", outstr);
debug!("Supervisor (E): {}", errstr);
debug!("Supervisor Code {:?}", output.status.code());
return Err(bldr_error!(ErrorKind::SupervisorSignalFailed));
}
Err(_) => {
debug!("No pidfile detected");
return Ok(None);
}
};

let mut f = try!(File::open(pid_file));
let mut contents = String::new();
try!(f.read_to_string(&mut contents));
debug!("pidfile contents = {}", contents);
let pid = match contents.parse::<u32>() {
Ok(pid) => pid,
Err(e) => {
debug!("Error reading pidfile: {}", e);
return Err(bldr_error!(ErrorKind::InvalidPidFile))
}
};
Ok(Some((pid, start_time)))
}

/// take a timestamp in seconds since epoch,
/// calculate how many seconds before right now
/// this timestamp occurs.
fn seconds_before_now(t0: Timespec) -> i64 {
let now = time::now().to_timespec();
(now - t0).num_seconds()
}

/// return a "down" or "run" with uptime in seconds status message
pub fn status_from_pid(&self, childinfo: Option<(u32, Timespec)>) -> BldrResult<String> {
match childinfo{
Some((pid, start_time)) => {
let diff = Self::seconds_before_now(start_time);
let s = format!("run: (pid {}) {}s\n", pid, diff);
Ok(s)
}
None => Ok("down".to_string()),
}
}

/// Get status of running package.
pub fn status(&self) -> BldrResult<String> {
self.signal(Signal::Status)
/// read the pidfile to get a status
pub fn status_via_pidfile(&self) -> BldrResult<String> {
let pidinfo = try!(self.read_pidfile());
self.status_from_pid(pidinfo)
}

/// A vector of ports we expose
Expand Down Expand Up @@ -457,7 +465,7 @@ impl Package {
self.release)
}

/// Run iniitalization hook if present
/// Run initialization hook if present
pub fn initialize(&self, context: &ServiceConfig) -> BldrResult<()> {
if let Some(hook) = self.hooks().init_hook {
match hook.run(Some(context)) {
Expand All @@ -469,31 +477,26 @@ impl Package {
}
}

/// Run reconfigure hook if present
/// Run reconfigure hook if present, DOES NOT restart the package
pub fn reconfigure(&self, context: &ServiceConfig) -> BldrResult<()> {
if let Some(hook) = self.hooks().reconfigure_hook {
match hook.run(Some(context)) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
} else {
match self.signal(Signal::Restart) {
Ok(_) => Ok(()),
Err(e) => {
let output = format!("failed to run default hook: {}", e);
Err(bldr_error!(ErrorKind::HookFailed(HookType::Reconfigure, -1, output)))
}
}
Ok(())
}
}

/// called from outside a topo worker, this will hit the pidfile
pub fn supervisor_running(&self) -> bool {
let res = self.signal(Signal::Status);
let res = self.status_via_pidfile();
match res {
Ok(_) => return true,
Err(e) => {
debug!("Supervisor not running?: {:?}", e);
return false;
return false
}
}
}
Expand All @@ -520,9 +523,11 @@ impl Package {
Err(e) => Err(BldrError::from(e)),
}
} else {
let status_output = try!(self.signal(Signal::Status));
let status_output = try!(self.status_via_pidfile());
let last_config = try!(self.last_config());
Ok(health_check::CheckResult::ok(format!("{}\n{}", status_output, last_config)))
Ok(health_check::CheckResult::ok(format!("{}\n{}",
status_output,
last_config)))
}
}

Expand Down Expand Up @@ -638,7 +643,10 @@ impl Package {

impl Into<PackageIdent> for Package {
fn into(self) -> PackageIdent {
PackageIdent::new(self.origin, self.name, Some(self.version), Some(self.release))
PackageIdent::new(self.origin,
self.name,
Some(self.version),
Some(self.release))
}
}

Expand Down
2 changes: 1 addition & 1 deletion components/bldr/src/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ fn config(lock: &Arc<RwLock<Package>>, _req: &mut Request) -> IronResult<Respons
/// * Fails if the supervisor cannot return the status.
fn status(lock: &Arc<RwLock<Package>>, _req: &mut Request) -> IronResult<Response> {
let package = lock.read().unwrap();
let output = try!(package.status());
let output = try!(package.status_via_pidfile());
Ok(Response::with((status::Ok, output)))
}

Expand Down
10 changes: 5 additions & 5 deletions components/bldr/src/topology/leader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software
// is made available under an open source license such as the Apache 2.0 License.

use topology::{self, standalone, State, Worker};
use topology::{self, standalone, State, Worker, stop};
use state_machine::StateMachine;
use error::{BldrResult, BldrError};
use package::Package;
use config::Config;
use census::MIN_QUORUM;
use package::Signal;
use gossip::server;

static LOGKEY: &'static str = "TL";
Expand Down Expand Up @@ -125,9 +124,10 @@ fn state_check_for_election(worker: &mut Worker) -> BldrResult<(State, u64)> {
}
outputln!("Stopping the service to ensure there is only one master");
{
let package = worker.package.write().unwrap();
if let Err(e) = package.signal(Signal::Stop) {
outputln!("{}", e);
if worker.child_info.is_some() {
if let Err(e) = stop(worker.child_info.as_ref().unwrap().pid) {
outputln!("{}", e);
}
}
}
} else {
Expand Down
Loading

0 comments on commit 021bec8

Please sign in to comment.