Skip to content

Commit

Permalink
refactor lifecycle stuff into topo
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Parfitt committed Mar 24, 2016
1 parent 01c493f commit 03f1ea5
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 123 deletions.
117 changes: 28 additions & 89 deletions components/bldr/src/package/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,6 @@ const RECONFIGURE_FILENAME: &'static str = "reconfigure";
const RUN_FILENAME: &'static str = "run";
const PIDFILE_NAME: &'static str = "PID";

/// Tell the package to stop or send it a signal.
/// Starting/restarting packages is handled by the topology.
#[derive(Debug)]
pub enum PkgNotification {
Stop,
Kill,
Status,
UnixSignal {
sig: signals::Signal,
},
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Package {
pub origin: String,
Expand All @@ -64,11 +52,6 @@ pub struct Package {
pub tdeps: Vec<PackageIdent>,
}

/// PID and start time of a child process
pub struct PidfileInfo {
pub pid: i32,
pub start_time: i64,
}

impl Package {
pub fn deps<P: AsRef<Path>>(ident: &PackageIdent, home: P) -> BldrResult<Vec<PackageIdent>> {
Expand Down Expand Up @@ -224,7 +207,7 @@ impl Package {
/// 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<PidfileInfo>> {
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);
Expand All @@ -234,7 +217,7 @@ impl Package {
let start_time = match fs::metadata(&pid_file) {
Ok(metadata) => {
if metadata.is_file() {
metadata.ctime()
Timespec::new(metadata.ctime(),0 /* nanos */)
} else {
return Ok(None);
}
Expand All @@ -249,98 +232,54 @@ impl Package {
let mut contents = String::new();
try!(f.read_to_string(&mut contents));
debug!("pidfile contents = {}", contents);
let pid = try!(contents.parse::<i32>());
Ok(Some(PidfileInfo {
pid: pid,
start_time: start_time,
}))
let pid = try!(contents.parse::<u32>());
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: i64) -> i64 {
let t0 = Timespec::new(t0, 0 /* nanos */);
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 do_status(&self, pidfileinfo: Option<PidfileInfo>) -> BldrResult<String> {
match pidfileinfo {
Some(pi) => {
let diff = Self::seconds_before_now(pi.start_time);
let s = format!("run: (pid {}) {}s\n", pi.pid, diff);
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()),
}
}

/// 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)
}

/// Pass through a Unix signal to the child process
pub fn do_unix_signal(&self, pid: i32, sig: signals::Signal) -> BldrResult<()> {
debug!("do_unix_signal");
try!(signals::send_signal_to_pid(pid, sig));
pub fn send_unix_signal(&self, pid: u32, sig: signals::Signal) -> BldrResult<()> {
try!(signals::send_signal_to_pid(pid as i32, sig));
Ok(())
}

/// Send a SIGINT to the child
pub fn do_stop(&self, pid: i32) -> BldrResult<()> {
debug!("do_stop");
try!(signals::send_signal_to_pid(pid, signals::Signal::SIGINT));
pub fn stop(&self, pid: u32) -> BldrResult<()> {
try!(signals::send_signal_to_pid(pid as i32, signals::Signal::SIGINT));
Ok(())
}

/// send a SIGKILL to the child
pub fn do_force_stop(&self, pid: i32) -> BldrResult<()> {
debug!("do_force_stop");
try!(signals::send_signal_to_pid(pid, signals::Signal::SIGKILL));
pub fn force_stop(&self, pid: u32) -> BldrResult<()> {
try!(signals::send_signal_to_pid(pid as i32, signals::Signal::SIGKILL));
Ok(())
}

/// send a "notification" to a package. Most notifications will fail
/// if there isn't a valid PID file, with the exception of Status,
/// which will return "down" if the pidfile does not exist.
pub fn notify(&self, notification: PkgNotification) -> BldrResult<Option<String>> {
let pidfileinfo = try!(self.read_pidfile());
debug!("Received notification = {:?}", notification);

// You can check the status even if the pidfile is missing
if let PkgNotification::Status = notification {
let result = try!(self.do_status(pidfileinfo));
return Ok(Some(result));
}

if pidfileinfo.is_none() {
debug!("No child process to notify");
return Ok(None);
}

let pidfileinfo = pidfileinfo.unwrap();
let pid = pidfileinfo.pid;

match notification {
PkgNotification::Stop => {
try!(self.do_stop(pid));
Ok(None)
}
PkgNotification::Kill => {
try!(self.do_force_stop(pid));
Ok(None)
}
PkgNotification::UnixSignal { sig } => {
try!(self.do_unix_signal(pid, sig));
Ok(None)
}
PkgNotification::Status => Ok(None), // unreachable
}
}

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

/// A vector of ports we expose
pub fn exposes(&self) -> Vec<String> {
match fs::metadata(self.join_path("EXPOSES")) {
Expand Down Expand Up @@ -541,7 +480,7 @@ impl Package {
self.release)
}

/// Run initalization 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 @@ -565,13 +504,14 @@ impl Package {
}
}

/// called from outside a topo worker, this will hit the pidfile
pub fn supervisor_running(&self) -> bool {
let res = self.notify(PkgNotification::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 @@ -598,11 +538,10 @@ impl Package {
Err(e) => Err(BldrError::from(e)),
}
} else {
let status_output = try!(self.notify(PkgNotification::Status));
let status_output = try!(self.status_via_pidfile());
let last_config = try!(self.last_config());
// Status will always return a string, even if the service is down
Ok(health_check::CheckResult::ok(format!("{}\n{}",
status_output.unwrap(),
status_output,
last_config)))
}
}
Expand Down
3 changes: 1 addition & 2 deletions components/bldr/src/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +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 result = try!(package.status());
let output = result.unwrap_or("".to_string());
let output = try!(package.status_via_pidfile());
Ok(Response::with((status::Ok, output)))
}

Expand Down
7 changes: 4 additions & 3 deletions components/bldr/src/topology/leader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use error::{BldrResult, BldrError};
use package::Package;
use config::Config;
use census::MIN_QUORUM;
use package::PkgNotification;
use gossip::server;

static LOGKEY: &'static str = "TL";
Expand Down Expand Up @@ -126,8 +125,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.notify(PkgNotification::Stop) {
outputln!("{}", e);
if worker.child_info.is_some() {
if let Err(e) = package.stop(worker.child_info.as_ref().unwrap().pid) {
outputln!("{}", e);
}
}
}
} else {
Expand Down
Loading

1 comment on commit 03f1ea5

@chef-delivery
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.