diff --git a/.travis.yml b/.travis.yml index cbb56e15ef..be270c0461 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ matrix: language: rust env: - COMPONENTS=bin LIBSODIUM_PREFIX=$HOME/pkgs/libsodium/1.0.8 LIBARCHIVE_PREFIX=$HOME/pkgs/libarchive/3.2.0 LIBZMQ_PREFIX=$HOME/pkgs/zeromq/4.1.4 PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$LIBARCHIVE_PREFIX/lib/pkgconfig:$LIBSODIUM_PREFIX/lib/pkgconfig:$LIBZMQ_PREFIX/lib/pkgconfig" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$LIBARCHIVE_PREFIX/lib:$LIBSODIUM_PREFIX/lib:$LIBZMQ_PREFIX/lib" LIBRARY_PATH="$LIBRARY_PATH:$LIBZMQ_PREFIX/lib" - - AFFECTED_DIRS="components/director|components/hab|components/sup" + - AFFECTED_DIRS="components/hab|components/sup" rust: stable sudo: false addons: diff --git a/Cargo.lock b/Cargo.lock index 5a43424a4f..8081039043 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -554,27 +554,6 @@ dependencies = [ "url 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "habitat_director" -version = "0.0.0" -dependencies = [ - "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "habitat_common 0.0.0", - "habitat_core 0.0.0", - "habitat_sup 0.0.0", - "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wonder 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "habitat_http_client" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 4f4b41db7a..e711adb994 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ members = [ "components/builder-worker", "components/common", "components/core", - "components/director", "components/hab", "components/http-client", "components/net", diff --git a/Makefile b/Makefile index 18c28c64ea..edd9124372 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ else docs_host := 127.0.0.1 endif -BIN = director hab hab-butterfly sup +BIN = hab hab-butterfly sup LIB = butterfly builder-dbcache builder-core builder-protocol common core builder-depot-client http-client net SRV = builder-api builder-admin builder-depot builder-router builder-jobsrv builder-sessionsrv builder-vault builder-worker ALL = $(BIN) $(LIB) $(SRV) diff --git a/RELEASE.md b/RELEASE.md index fc4cf75d12..b90dd7758b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -135,7 +135,7 @@ target component. $ cat build.manifest | while read entry; do echo "Building $(echo $entry | cut -d ' ' -f 1)"; build $(echo $entry | cut -d ' ' -f 2) || break; done ``` -Next we will be running the same commands for hab-butterfly, hab-sup, hab-sup-static and hab-director. +Next we will be running the same commands for hab-butterfly, hab-sup, and hab-sup-static. ### Build `hab-butterfly` @@ -173,24 +173,6 @@ Next we will be running the same commands for hab-butterfly, hab-sup, hab-sup-st > cat build.manifest | while read entry; do echo "Building $(echo $entry | cut -d ' ' -f 1)"; build $(echo $entry | cut -d ' ' -f 2) || break; done ``` -### Build `hab-director` - -1. From outside of a studio: - - ``` - $ cd ~/code - $ rm build.manifest - $ find core-plans habitat -name plan.sh | ./core-plans/bin/build-dependent-order.rb core/hab-director > build.manifest - ``` - -1. From within a studio: - - ``` - $ hab studio enter - > build habitat/components/director - > cat build.manifest | while read entry; do echo "Building $(echo $entry | cut -d ' ' -f 1)"; build $(echo $entry | cut -d ' ' -f 2) || break; done - ``` - ## Build Mac Components 1. Ensure no pre-existing old virtual machine, then turn on and enter the system diff --git a/components/director/Cargo.toml b/components/director/Cargo.toml deleted file mode 100644 index 77967656ec..0000000000 --- a/components/director/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "habitat_director" -version = "0.0.0" -authors = ["Adam Jacob ", "Jamie Winsor ", "Fletcher Nichol ", "Joshua Timberman ", "Dave Parfitt "] -build = "../build.rs" -workspace = "../../" - -[lib] -name = "habitat_director" - -[[bin]] -name = "hab-director" -doc = false - -[[test]] -name = "functional" - -[dependencies] -ansi_term = "*" -env_logger = "*" -libc = "*" -log = "*" -regex = "*" -rustc-serialize = "*" -time = "*" -toml = "*" -wonder = "*" - -[dependencies.clap] -version = "*" -features = [ "suggestions", "color", "unstable" ] - -[dependencies.habitat_core] -path = "../core" - -[dependencies.habitat_common] -path = "../common" - -[dependencies.habitat_sup] -path = "../sup" - -[dependencies.uuid] -version = "*" -features = ["rustc-serialize"] - -[dev-dependencies] -tempdir = "*" - -[features] -functional = [] diff --git a/components/director/README.md b/components/director/README.md deleted file mode 100644 index 55a8f3a6c6..0000000000 --- a/components/director/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Habitat Director - -Please see `src/main.rs` for documentation. - - diff --git a/components/director/habitat/default.toml b/components/director/habitat/default.toml deleted file mode 100644 index 5b30192896..0000000000 --- a/components/director/habitat/default.toml +++ /dev/null @@ -1 +0,0 @@ -path = "/hab/svc/hab-director/data" diff --git a/components/director/habitat/plan.sh b/components/director/habitat/plan.sh deleted file mode 100644 index 6ab9248a57..0000000000 --- a/components/director/habitat/plan.sh +++ /dev/null @@ -1,66 +0,0 @@ -pkg_name=hab-director -pkg_origin=core -pkg_version=$(cat "$PLAN_CONTEXT/../../../VERSION") -pkg_maintainer="The Habitat Maintainers " -pkg_license=('Apache-2.0') -pkg_source=nosuchfile.tar.gz -pkg_deps=(core/glibc core/gcc-libs core/libarchive core/libsodium core/openssl core/zeromq) -pkg_build_deps=(core/coreutils core/cacerts core/rust core/gcc) -pkg_bin_dirs=(bin) -bin="hab-director" -pkg_svc_run="$bin start -c ${pkg_svc_path}/config.toml" -pkg_svc_user="root" - -do_prepare() { - # Can be either `--release` or `--debug` to determine cargo build strategy - build_type="--release" - build_line "Building artifacts with \`${build_type#--}' mode" - - export rustc_target="x86_64-unknown-linux-gnu" - build_line "Setting rustc_target=$rustc_target" - - # Used by the `build.rs` program to set the version of the binaries - export PLAN_VERSION="${pkg_version}/${pkg_release}" - build_line "Setting PLAN_VERSION=$PLAN_VERSION" - - # Used by Cargo to use a pristine, isolated directory for all compilation - export CARGO_TARGET_DIR="$HAB_CACHE_SRC_PATH/$pkg_dirname" - build_line "Setting CARGO_TARGET_DIR=$CARGO_TARGET_DIR" - - export LIBARCHIVE_LIB_DIR=$(pkg_path_for libarchive)/lib - export LIBARCHIVE_INCLUDE_DIR=$(pkg_path_for libarchive)/include - export LIBZMQ_PREFIX=$(pkg_path_for zeromq) - export OPENSSL_LIB_DIR=$(pkg_path_for openssl)/lib - export OPENSSL_INCLUDE_DIR=$(pkg_path_for openssl)/include - export SODIUM_LIB_DIR=$(pkg_path_for libsodium)/lib -} - -do_build() { - pushd $PLAN_CONTEXT > /dev/null - cargo build ${build_type#--debug} --target=$rustc_target --verbose - popd > /dev/null -} - -do_install() { - install -v -D $CARGO_TARGET_DIR/$rustc_target/${build_type#--}/$bin \ - $pkg_prefix/bin/$bin -} - -do_strip() { - if [[ "$build_type" != "--debug" ]]; then - do_default_strip - fi -} - -# Turn the remaining default phases into no-ops -do_download() { - return 0 -} - -do_verify() { - return 0 -} - -do_unpack() { - return 0 -} diff --git a/components/director/src/config.rs b/components/director/src/config.rs deleted file mode 100644 index 6b2e9c0d30..0000000000 --- a/components/director/src/config.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use std::collections::HashMap; -use std::net::SocketAddrV4; -use std::str::FromStr; - -use hcore::config::ConfigFile; - -use toml; -use error::{Error, Result}; -use super::ServiceDef; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Config { - /// Service defs read from config.toml - pub service_defs: Vec, - /// We might not be running under a Habitat supervisor - pub dir_sup_listen: Option, -} - -impl ConfigFile for Config { - type Error = Error; - - fn from_toml(toml: toml::Value) -> Result { - let mut cfg = Config::default(); - - cfg.dir_sup_listen = try!(Config::parse_gossip_ip_port(&toml)); - - let c = match toml.lookup("cfg.services") { - Some(c) => c, - None => return Err(Error::NoServices), - }; - - let paths = Config::traverse(c); - - for p in &paths { - let mut sd = ServiceDef::from_str(p).unwrap(); - println!("Loaded service def: {}", &sd.to_string()); - let sdname = sd.to_string(); - sd.cli_args = try!(Self::lookup_service_param(&toml, &sdname, "start")); - sd.env = try!(Self::lookup_service_param_table(&toml, &sdname, "env")); - sd.ident.release = try!(Self::lookup_service_param(&toml, &sdname, "release")); - sd.ident.version = try!(Self::lookup_service_param(&toml, &sdname, "version")); - cfg.service_defs.push(sd); - } - Ok(cfg) - } -} - -impl Default for Config { - fn default() -> Self { - Config { - service_defs: Vec::new(), - dir_sup_listen: None, - } - } -} - -impl Config { - /// try to parse sys.gossip and sys.gossip_port. - /// If we're not running under a supervisor, these values will - /// most likely be empty. - fn parse_gossip_ip_port(toml: &toml::Value) -> Result> { - let ip = toml.lookup("sys.gossip_ip") - .and_then(|s| s.as_str()); - let port = toml.lookup("sys.gossip_port") - .and_then(|s| s.as_integer()); - match (ip, port) { - (Some(ip), Some(port)) => { - let ip_port = format!("{}:{}", ip, port); - match SocketAddrV4::from_str(&ip_port) { - Ok(addr) => Ok(Some(addr)), - Err(e) => { - let msg = format!("Can't parse gossip ip:port ({}): {}", ip_port, e); - Err(Error::DirectorError(msg)) - } - } - } - _ => Ok(None), - } - } - - /// Perform a lookup on a dynamic toml path as part of a service. - /// For example, for a value `valuename` in service name - /// `origin.name.group.org`, we'll perform a lookup on - /// `cfg.services.origin.name.group.org.valuename` and return - /// a Some(string) if it's available. - fn lookup_service_param(toml: &toml::Value, - service_name: &str, - param_name: &str) - -> Result> { - let key = format!("cfg.services.{}.{}", service_name, param_name); - debug!("Looking up param {}", &key); - if let Some(v) = toml.lookup(&key) { - if v.type_str() != "string" { - let msg = format!("{} value must be a valid TOML string", &key); - return Err(Error::DirectorError(msg)); - } - - if let Some(v) = v.as_str() { - return Ok(Some(v.to_string())); - } - } - Ok(None) - } - - /// Perform a lookup on a dynamic toml path as part of a service. - /// For example, for a value `valuename` in service name - /// `origin.name.group.org`, we'll perform a lookup on - /// `cfg.services.origin.name.group.org.valuename` and return - /// a Some(string) if it's available. - fn lookup_service_param_table(toml: &toml::Value, - service_name: &str, - param_name: &str) - -> Result> { - let key = format!("cfg.services.{}.{}", service_name, param_name); - debug!("Looking up table {}", &key); - let mut kvs: HashMap = HashMap::new(); - if let Some(k) = toml.lookup(&key) { - if let Some(tbl) = k.as_table() { - for (ref k, ref v) in tbl.iter() { - let k = k.to_string(); - if v.type_str() != "string" { - let msg = format!("env entry for {} must be a valid TOML string", &key); - return Err(Error::DirectorError(msg)); - } - let v = v.as_str().map_or("".to_string(), |v| v.to_string()); - debug!("table param {} = {}", &k, &v); - kvs.insert(k, v); - } - } - } - Ok(kvs) - } - - - /// traverse a toml tree of Tables, return a list of - /// Strings for each unique path - fn traverse(v: &toml::Value) -> Vec { - fn _traverse(v: &toml::Value, path: &mut Vec, paths: &mut Vec) { - // allow processing of paths longer than - // ... - let current_path = path.join("."); - if let Some(tbl) = v.as_table() { - if path.len() == 4 { - paths.push(current_path); - return; - } - // return if this table doesn't have any child tables - // for path lengths < 4 - if tbl.values().all(|ref v| v.as_table().is_none()) { - paths.push(current_path); - return; - } - for (k, v) in tbl.iter() { - path.push(k.clone()); - _traverse(v, path, paths); - let _ = path.pop(); - } - } - } - - let mut path: Vec = Vec::new(); - let mut paths: Vec = Vec::new(); - _traverse(v, &mut path, &mut paths); - paths - } -} - -#[cfg(test)] -mod tests { - use toml; - - use hcore::config::ConfigFile; - use super::*; - - #[test] - fn test_parse_traversal() { - // NOTE: these toml tables DO NOT contain the cfg.services prefix, - // that will be pulled out via toml.lookup() - let service_toml = r#" - [core.redis.somegroup.someorg] - start = "foo" - [core.rngd.foo.someorg] - start = "bar" - [myorigin.xyz.foo.otherorg] - [foo] - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - let paths = Config::traverse(&root); - assert!(paths.contains(&"core.redis.somegroup.someorg".to_string())); - assert!(paths.contains(&"core.rngd.foo.someorg".to_string())); - assert!(paths.contains(&"myorigin.xyz.foo.otherorg".to_string())); - // segment length doesn't matter, we'll filter those out later - assert!(paths.contains(&"foo".to_string())); - } - - #[test] - fn test_from_toml_with_gossip_ip_port() { - let service_toml = r#" - - [sys] - gossip_ip="192.168.1.5" - gossip_port=1234 - [cfg.services.core.redis.somegroup.someorg] - start = "foo" - - # Comment - [cfg.services.core.rngd.foo.someorg] - start = "bar" - # release + version must be in double quotes! - release="20160427205048" - version="1.1" - - [cfg.services.myorigin.xyz.foo.otherorg] - - [cfg.foo] - # foo should be skipped. check the count of services, we should have - # 3, not 4. - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - let cfg = Config::from_toml(root).unwrap(); - - assert_eq!("192.168.1.5:1234", - cfg.dir_sup_listen.as_ref().unwrap().to_string()); - assert_eq!(3, cfg.service_defs.len()); - - // first service - assert_eq!("core.redis.somegroup.someorg", - cfg.service_defs[0].to_string()); - assert_eq!(Some("foo".to_string()), cfg.service_defs[0].cli_args); - assert_eq!(None, cfg.service_defs[0].ident.release); - assert_eq!(None, cfg.service_defs[0].ident.version); - - // second service - assert_eq!("core.rngd.foo.someorg", cfg.service_defs[1].to_string()); - assert_eq!(Some("bar".to_string()), cfg.service_defs[1].cli_args); - assert_eq!(Some("20160427205048".to_string()), - cfg.service_defs[1].ident.release); - assert_eq!(Some("1.1".to_string()), cfg.service_defs[1].ident.version); - - // third service - assert_eq!("myorigin.xyz.foo.otherorg", cfg.service_defs[2].to_string()); - assert_eq!(None, cfg.service_defs[2].cli_args); - assert_eq!(None, cfg.service_defs[2].ident.release); - assert_eq!(None, cfg.service_defs[2].ident.version); - - } - - #[test] - #[should_panic(expected = "cfg.services.core.redis.somegroup.someorg.start value must be a valid TOML string")] - fn test_from_toml_with_invalid_start() { - let service_toml = r#" - [cfg.services.core.redis.somegroup.someorg] - start = 1 - # start MUST be a string - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - Config::from_toml(root).unwrap(); - } - - - #[test] - fn test_from_toml_without_gossip_listen() { - let service_toml = r#" - [cfg.services.core.redis.somegroup.someorg] - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - let cfg = Config::from_toml(root).unwrap(); - assert_eq!(None, cfg.dir_sup_listen); - assert_eq!(1, cfg.service_defs.len()); - } - - #[test] - fn test_env_params() { - let service_toml = r#" - [cfg.services.core.rngd.foo.someorg.env] - JAVA_HOME="/dev/null" - TCL_HOME="/bin/false" - [cfg.services.myorigin.xyz.foo.otherorg] - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - let cfg = Config::from_toml(root).unwrap(); - - let sd0 = &cfg.service_defs[0]; - assert_eq!(2, sd0.env.len()); - assert!(sd0.env.contains_key("JAVA_HOME")); - assert!(sd0.env.contains_key("TCL_HOME")); - assert_eq!("/dev/null", sd0.env.get("JAVA_HOME").unwrap()); - assert_eq!("/bin/false", sd0.env.get("TCL_HOME").unwrap()); - - let sd1 = &cfg.service_defs[1]; - assert_eq!(0, sd1.env.len()); - } - - - #[test] - #[should_panic(expected = "env entry for cfg.services.core.rngd.foo.someorg.env must be a valid TOML string")] - fn test_env_params_invalid_string() { - let service_toml = r#" - [cfg.services.core.rngd.foo.someorg.env] - JAVA_HOME=1 - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - Config::from_toml(root).unwrap(); - } - -} diff --git a/components/director/src/controller.rs b/components/director/src/controller.rs deleted file mode 100644 index 78198cd8cb..0000000000 --- a/components/director/src/controller.rs +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::net::Ipv4Addr; -use std::net::SocketAddrV4; -use std::str::FromStr; -use std::thread; -use std::time::Duration; - -use time::SteadyTime; - -use error::Result; -use hcore::util::sys::ip; -use hsup::manager::signals; - -use super::Config; -use super::task::{Task, ExecContext, ExecParams}; - -/// we check child processes at most once every MINIMUM_LOOP_TIME_MS -static MINIMUM_LOOP_TIME_MS: i64 = 200; -static LOGKEY: &'static str = "CTRL"; -pub const FIRST_GOSSIP_PORT: u16 = 9000; -pub const FIRST_HTTP_PORT: u16 = 8000; - -pub struct Controller { - pub config: Config, - pub exec_ctx: ExecContext, - pub children: Option>, -} - -impl Controller { - pub fn new(config: Config, exec_ctx: ExecContext) -> Controller { - signals::init(); - - Controller { - config: config, - exec_ctx: exec_ctx, - children: None, - } - } - - /// iterate through the config ServiceDefs and create `Task` - /// instances. A Controller contains "all the tasks", so - /// it calculate gossip_port + http_port #s accordingly. - pub fn create_children(&mut self) -> Result<()> { - let mut children = Vec::new(); - let mut next_gossip_port = FIRST_GOSSIP_PORT; - let mut next_http_port = FIRST_HTTP_PORT; - - let default_ip = try!(ip()).to_string(); - let listen_ip = try!(Ipv4Addr::from_str(&default_ip)); - - let mut initial_peer: Option = self.config.dir_sup_listen; - - for sd in &self.config.service_defs { - let exec_ctx = self.exec_ctx.clone(); - let exec_params = ExecParams::new(SocketAddrV4::new(listen_ip, next_gossip_port), - SocketAddrV4::new(listen_ip, next_http_port), - initial_peer); - - // after the first iteration, each child will connect to the previous - initial_peer = Some(exec_params.gossip_listen.clone()); - - let dc = Task::new(exec_ctx, exec_params, sd.clone()); - children.push(dc); - - // this will have to be more intelligent if we - // let users define gossip/http ports - next_gossip_port += 1; - next_http_port += 1; - } - self.children = Some(children); - Ok(()) - } - - /// Process config to create children, then run in a loop forever. - pub fn start(&mut self) -> Result<()> { - try!(self.create_children()); - - if let None = self.children { - outputln!("No services defined"); - return Ok(()); - } else if self.children.as_ref().unwrap().len() == 0 { - outputln!("No services defined"); - return Ok(()); - } - - loop { - let start_time = SteadyTime::now(); - - // do the main loop "stuff" - if !try!(self.next_iteration()) { - // we received a signal, break out of this loop - break; - } - - // Slow down our loop - let elapsed_time = SteadyTime::now() - start_time; - let elapsed_millis = elapsed_time.num_milliseconds(); - - if elapsed_millis < MINIMUM_LOOP_TIME_MS { - let time = Duration::from_millis((MINIMUM_LOOP_TIME_MS - elapsed_millis) as u64); - thread::sleep(time); - } - } - Ok(()) - } - - /// This is called at each iteration in the self::start() loop. - /// It's pulled out into it's own function so it can be tested. - pub fn next_iteration(&mut self) -> Result { - match signals::check_for_signal() { - Some(signals::SignalEvent::Shutdown) => { - let mut children = self.children.as_mut().unwrap(); - for child in children.iter_mut() { - if let Some(pid) = child.pid { - outputln!("Sending SIGTERM to child {} (pid {})", - &child.service_def.to_string(), - &pid); - if let Err(e) = signals::send_signal(pid, signals::Signal::SIGTERM as u32) { - outputln!("Error sending SIGTERM to {} (pid {}): {}", - &child.service_def.to_string(), - &pid, - e); - - } - } - } - outputln!("Shutting down"); - return Ok(false); - } - Some(signals::SignalEvent::Passthrough(signal_code)) => { - let mut children = self.children.as_mut().unwrap(); - for child in children.iter_mut() { - if let Some(pid) = child.pid { - outputln!("Sending {:?} to child {} (pid {})", - &signal_code, - &child.service_def.to_string(), - &pid); - if let Err(e) = signals::send_signal(pid, signal_code) { - outputln!("Error sending {:?} to {} (pid {}): {}", - &signal_code, - &child.service_def.to_string(), - &pid, - e); - - } - } - } - } - None => {} - } - // leaving this here as I unwrap a couple lines down - // and I don't want to try and defeat the type system - if let None = self.children { - debug!("No children"); - return Ok(false); - } - let mut children = self.children.as_mut().unwrap(); - for child in children.iter_mut() { - if let Err(e) = child.check_process() { - outputln!("Failed to check child process {}: {}", - &child.service_def.to_string(), - e); - } - if child.is_down() { - match child.start() { - // the Task prints out a better "Started" message than - // we could (including ports etc) - Ok(_) => debug!("Started {}", &child.service_def.to_string()), - Err(e) => { - outputln!("Failed to start {}: {}", &child.service_def.to_string(), e) - } - }; - } - } - Ok(true) - } -} - -#[cfg(test)] -mod tests { - use std::path::PathBuf; - use time; - use toml; - - use hcore::config::ConfigFile; - use hcore::util::sys::ip; - use task::ExecContext; - use config::Config; - use super::*; - - fn get_test_config() -> Config { - let service_toml = r#" - [sys] - gossip_ip="192.168.1.2" - gossip_port=9876 - [cfg.services.core.redis.somegroup.someorg] - start = "foo" - [cfg.services.core.rngd.foo.someorg] - start = "bar" - [cfg.services.myorigin.xyz.foo.otherorg] - [foo] - "#; - - let root: toml::Value = service_toml.parse().unwrap(); - Config::from_toml(root).unwrap() - } - - // call a closure in a loop until it returns true - // or timeout after 30 seconds and return false - pub fn wait_until_true(c: &mut T, some_fn: F) -> bool - where F: Fn(&mut T) -> bool - { - let wait_duration = time::Duration::seconds(30); - let current_time = time::now_utc().to_timespec(); - let stop_time = current_time + wait_duration; - while time::now_utc().to_timespec() < stop_time { - if some_fn(c) { - return true; - } - } - false - } - - /// parse some config, make sure the arguments are generated correctly - /// and then start some child processes and see if they restart - /// when killed. We don't start full hab-sup processes. - /// - /// NOTE: The controller uses an instance of SignalNotifier, - /// which is a Wonder actor that catches OS signals for us. - /// It can only be started once per process (in "this" test process), - /// so we can't currently share it between tests. Also, as tests - /// are run concurrently, it wouldn't be possible to use a shared - /// instance of SignalNotifier anyways. - #[test] - fn test_controller_with_sup_parent() { - let mut ec = ExecContext::default(); - ec.sup_path = PathBuf::from("/bin/false"); - ec.service_root = PathBuf::from("/tmp"); - - let config = get_test_config(); - let mut controller = Controller::new(config, ec); - controller.create_children().unwrap(); - assert_eq!(3, controller.children.as_ref().unwrap().len()); - - let test_ip = ip().unwrap().to_string(); - { - let child = &controller.children.as_ref().unwrap()[0]; - let args = child.get_cmd_args().unwrap(); - assert_eq!(args.as_slice(), - ["start", - "core/redis", - "foo", - "--listen-gossip", - format!("{}:9000", test_ip).as_str(), - "--listen-http", - format!("{}:8000", test_ip).as_str(), - "--group", - "somegroup", - "--org", - "someorg", - "--peer", - "192.168.1.2:9876"]); - } - { - let child = &controller.children.as_ref().unwrap()[1]; - let args = child.get_cmd_args().unwrap(); - assert_eq!(args.as_slice(), - ["start", - "core/rngd", - "bar", - "--listen-gossip", - // did we increment the port? - format!("{}:9001", test_ip).as_str(), - "--listen-http", - // did we increment the port? - format!("{}:8001", test_ip).as_str(), - "--group", - "foo", - "--org", - "someorg", - "--peer", - // is the peer set to the previous port? - format!("{}:9000", test_ip).as_str()]); - } - { - let child = &controller.children.as_ref().unwrap()[2]; - let args = child.get_cmd_args().unwrap(); - - assert_eq!(args.as_slice(), - ["start", - "myorigin/xyz", - "--listen-gossip", - // did we increment the port? - format!("{}:9002", test_ip).as_str(), - "--listen-http", - // did we increment the port? - format!("{}:8002", test_ip).as_str(), - "--group", - "foo", - "--org", - "otherorg", - "--peer", - // is the peer set to the previous port? - format!("{}:9001", test_ip).as_str()]); - } - - controller.next_iteration().unwrap(); - - assert_eq!(1, controller.children.as_ref().unwrap()[0].starts); - // We gave the child process bad args, so it won't start. - // Lets wait for 30 seconds to see if we register restarts for the - // children - // let killpid = &controller.children.as_ref().unwrap()[0].pid.unwrap(); - // signals::send_signal_to_pid(*killpid, signals::Signal::SIGKILL).unwrap(); - assert!(wait_until_true(&mut controller, |c| { - c.next_iteration().unwrap(); - c.children.as_ref().unwrap()[0].starts > 1 && - c.children.as_ref().unwrap()[1].starts > 1 && - c.children.as_ref().unwrap()[2].starts > 1 - })); - } -} diff --git a/components/director/src/error.rs b/components/director/src/error.rs deleted file mode 100644 index 4ac86f0542..0000000000 --- a/components/director/src/error.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::error; -use std::io; -use std::fmt; -use std::net; -use std::result; - -use hcommon; -use hcore; - -#[derive(Debug)] -pub enum Error { - AddrParseError(net::AddrParseError), - DirectorError(String), - HabitatCommon(hcommon::Error), - HabitatCore(hcore::Error), - IO(io::Error), - NoServices, - RootRequired, -} - -pub type Result = result::Result; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match *self { - Error::AddrParseError(ref e) => format!("Can't parse IP address {}", e), - Error::DirectorError(ref e) => format!("Director error: {}", e), - Error::HabitatCommon(ref err) => format!("{}", err), - Error::HabitatCore(ref e) => format!("{}", e), - Error::IO(ref e) => format!("{}", e), - Error::NoServices => "No services specified in configuration".to_string(), - Error::RootRequired => { - "Root or administrator permissions required to complete operation".to_string() - } - }; - write!(f, "{}", msg) - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::AddrParseError(_) => "Can't parse IP address", - Error::DirectorError(_) => "Director Error", - Error::HabitatCommon(ref err) => err.description(), - Error::HabitatCore(ref err) => err.description(), - Error::IO(ref err) => err.description(), - Error::NoServices => "No services specified in configuration", - Error::RootRequired => { - "Root or administrator permissions required to complete operation" - } - } - } -} - -impl From for Error { - fn from(err: hcommon::Error) -> Error { - Error::HabitatCommon(err) - } -} - -impl From for Error { - fn from(err: hcore::Error) -> Error { - Error::HabitatCore(err) - } -} - - -impl From for Error { - fn from(err: io::Error) -> Error { - Error::IO(err) - } -} - -impl From for Error { - fn from(err: net::AddrParseError) -> Error { - Error::AddrParseError(err) - } -} diff --git a/components/director/src/lib.rs b/components/director/src/lib.rs deleted file mode 100644 index fa01739a52..0000000000 --- a/components/director/src/lib.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate habitat_common as hcommon; -extern crate habitat_core as hcore; -#[macro_use] -extern crate habitat_sup as hsup; -#[macro_use] -extern crate libc; -#[macro_use] -extern crate log; -#[macro_use] -extern crate rustc_serialize; -extern crate time; -extern crate toml; -extern crate wonder; - -pub mod config; -pub mod error; -pub mod task; -pub mod controller; - -pub use self::config::Config; -pub use self::error::{Error, Result}; - -use std::collections::HashMap; -use std::fmt; -use std::result; -use std::str::FromStr; - -use hcore::package::PackageIdent; -use hcore::service::ServiceGroup; - -/// ServiceDef is a combination of a PackageIdent and ServiceGroup -/// that a user has specified via config file. It represents -/// what the user wants to run, along with command line args and -/// any other params that a user can tweak via config. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ServiceDef { - pub ident: PackageIdent, - pub service_group: ServiceGroup, - pub cli_args: Option, - pub env: HashMap, -} - -impl ServiceDef { - pub fn new(ident: PackageIdent, service_group: ServiceGroup) -> ServiceDef { - ServiceDef { - ident: ident, - service_group: service_group, - cli_args: None, - env: HashMap::new(), - } - } -} - -impl fmt::Display for ServiceDef { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, - "{}.{}.{}{}", - &self.ident.origin, - &self.ident.name, - &self.service_group.group, - &self.service_group.dotted_org_or_empty()) - } -} - -impl AsRef for ServiceDef { - fn as_ref(&self) -> &ServiceDef { - self - } -} - -impl FromStr for ServiceDef { - type Err = Error; - - fn from_str(value: &str) -> result::Result { - let chunks: Vec<&str> = value.split(".").collect(); - let (origin, name, group, org) = match chunks.len() { - 3 => (chunks[0], chunks[1], chunks[2], None), - 4 => (chunks[0], chunks[1], chunks[2], Some(chunks[3].to_string())), - _ => return Err(Error::DirectorError(format!("Invalid service descriptor: {}", value))), - }; - - let ident = PackageIdent::new(origin, name, None, None); - let sg = ServiceGroup::new(name, group, org); - let sd = ServiceDef::new(ident, sg); - Ok(sd) - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - - #[test] - fn test_parse_service_without_org() { - let sd = ServiceDef::from_str("core.redis.somegroup").unwrap(); - assert_eq!("core.redis.somegroup", sd.to_string()); - assert_eq!("core", sd.ident.origin); - assert_eq!("redis", sd.ident.name); - assert_eq!(None, sd.ident.version); - assert_eq!(None, sd.ident.release); - assert_eq!("redis", sd.service_group.service); - assert_eq!("somegroup", sd.service_group.group); - assert_eq!(None, sd.service_group.organization); - } - - #[test] - fn test_parse_service_org() { - let sd = ServiceDef::from_str("core.redis.somegroup.someorg").unwrap(); - assert_eq!("core.redis.somegroup.someorg", sd.to_string()); - assert_eq!("core", sd.ident.origin); - assert_eq!("redis", sd.ident.name); - assert_eq!(None, sd.ident.version); - assert_eq!(None, sd.ident.release); - assert_eq!("redis", sd.service_group.service); - assert_eq!("somegroup", sd.service_group.group); - assert_eq!(Some("someorg".to_string()), sd.service_group.organization); - - - } - - #[test] - fn test_parse_bad_service_desc() { - assert!(ServiceDef::from_str("").is_err()); - assert!(ServiceDef::from_str("x").is_err()); - assert!(ServiceDef::from_str("x:y").is_err()); - assert!(ServiceDef::from_str("a.b").is_err()); - assert!(ServiceDef::from_str("a.b.c.d.e").is_err()); - } -} diff --git a/components/director/src/main.rs b/components/director/src/main.rs deleted file mode 100644 index a852b3c391..0000000000 --- a/components/director/src/main.rs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The Habitat Director is a supervisor for a group of `hab-sup` processes. -//! It loads packages to start from it's `config.toml` file. The director -//! will automatically restart child process upon failure detection. Each -//! child service runs in it's own `hab-sup` process. The director can be -//! run inside of a `hab-sup` instance as well. -//! -//! ### Components -//! - `Task` -//! - manages a hab-sup as a child process -//! - tracks a single child process PID -//! - generates CLI arguments for `hab-sup start` -//! - creates a PID file for it's child process -//! - starts a thread to read stdout from the child process -//! -//! - `Controller` -//! - A controller "has" and supervises many Tasks (children) -//! - calculates gossip and http port #'s for all children before starting. -//! - runs in a tight loop to see if children are down and start/restarts them. -//! - catches OS signals -//! -//! - `ExecContext` -//! - A task "execution context". The `ExecContext` is used to -//! decouple service root directory and path to a supervisor executable. -//! Decoupling these values into a struct allows us to easily test -//! `Tasks` + `Controllers`. -//! -//! - `ExecParams` -//! - Config values for a `Task` that the `Controller` calculates during -//! startup. `ExecParams` currently includes: -//! - gossip_listen -//! - http_listen -//! - Option -//! -//! - `ServiceDef` -//! - A combination of `PackageIdent`, `ServiceGroup`, and CLI args. These -//! values are loaded from the config file and are set by the user, as -//! opposed to `ExecContext` values which are set by the `Controller`. -//! - Examples: -//! - `core.redis.somegroup.someorg` corresponds to the -//! `core/redis` `PackageIdent`, and the -//! `redis.somegroup@someorg` `ServiceGroup`. -//! - `core.redis.somegroup` corresponds to the -//! `core/redis` `PackageIdent`, and the `redis.somegroup` `ServiceGroup` -//! (org-less). -//! -//! ``` -//! ┌──────────┐ -//! │ hab-sup │ -//! └──────────┘ -//! │ -//! │ -//! │ ┌───────────────┐ -//! │ │ │ -//! └───>│ Controller │────┐ ┌────────────┐ ┌────────┐ ┌──────────┐ -//! │ │ ├───>│ ExecParams │──>│ Task │───>│ hab-sup │ -//! └───────────────┘ │ └────────────┘ └────────┘ └──────────┘ -//! ┌─────────────┐ │ ┌────────────┐ ┌────────┐ ┌──────────┐ -//! │ ExecContext │────┼───>│ ExecParams │──>│ Task │───>│ hab-sup │ -//! └─────────────┘ │ └────────────┘ └────────┘ └──────────┘ -//! │ ┌────────────┐ ┌────────┐ ┌──────────┐ -//! └───>│ ExecParams │──>│ Task │───>│ hab-sup │ -//! └────────────┘ └────────┘ └──────────┘ -//! ``` -//! ### Config file format -//! -//! `ServiceDef`s are parsed from the `config.toml` file upon startup. -//! -//! All services must be described as children of the `services` toml table. -//! Note, when toml is rendered, the values for `services` will be -//! located under `cfg.services.*`. -//! -//! Each service definition is a `.` separated list of values as a TOML table name. -//! -//! `[services....]` -//! -//! or rendered by `hab-sup`: -//! -//! `[cfg.services....]` -//! -//! A service definition can additionally specify a `start` key/value under -//! the service table definition: -//! -//! ``` -//! # Start core/redis with --group somegroup and --org someorg -//! # Additionally, pass in --permanent-peer to the start CLI -//! [cfg.services.core.redis.somegroup.someorg] -//! start = "--permanent-peer" -//! -//! [cfg.services.core.rngd.foo.someorg] -//! start = "--permanent-peer --foo=bar" -//! -//! Environment variables can be specified for each service in a TOML -//! table. Only TOML string values are supported as environment variables. -//! -//! ``` -//! [cfg.services.core.someservice.somegroup.someorg] -//! start = "--permanent-peer" -//! [cfg.services.core.someservice.somegroup.someorg.env] -//! JAVA_HOME="/hab/pkgs/core/jdk/foo" -//! CLASSPATH="/hab/pkgs/core/bar/jars" -//! -//! ``` -//! ### Signal handling -//! -//! - If started from bash: when hab-director receives SIGINT or SIGTERM, -//! the director exits. Child processes will have already been sent the signal -//! from bash because they are in the same session group, and will die as well. -//! - TODO: If NOT started from bash: when hab-director receives SIGINT or SIGTERM, -//! signal behavior is undefined and signals are NOT forwarded to child tasks. -//! - When hab-director receives any other signal (that doesn't -//! kill *this* process), they are re-sent to each `Task` in the same -//! order that services are defined in `config.toml`. -//! -//! ### PID files -//! -//! - for each `Task` created, a pid file is created in the `hab-director` -//! service directory. For example, for `core.redis.somegroup.someorg`, we'll -//! have a `/hab/svc/hab-director/core-redis-somegroup-someorg.pid` file -//! created. Creation/removal of these files is handled automatically by -//! the director. -//! -//! ### Ring behavior + port assignment -//! -//! - The first task that is created will attempt to join -//! `sys.gossip_ip`:`sys.gossip_port` by specifying a `--peer` to hab-sup. -//! This only occurs if the director is running under a supervisor. -//! - Each subsequent task that is created uses the previous tasks IP:port as -//! a value for --peer. -//! - Gossip port numbers are assigned starting with FIRST_GOSSIP_PORT (9000) -//! - HTTP port numbers are assigned starting with FIRST_HTTP_PORT (8000) -//! - If the hab-sup that's running the director is assigned ports other than -//! the defaults (9638, 9631), there is a possibility that they could conflict -//! with the automatically assigned port numbers of the child tasks. -//! - The diagram below shows a hab-sup process running the director with it's -//! default IP + port (changeable by the user). Each task that's started is -//! assigned a new consecutive gossip + http IP. -//! -//! ┌────────────────────────────┐ -//! │ hab-sup (Director) │ -//! ┌─>│ Gossip = 9638 │ * default ports -//! │ │ HTTP = 9631 │ -//! │ └────────────────────────────┘ -//! │ -//! Peer │ -//! │ ┌────────────────────────────┐ -//! │ │Task 0 │ -//! └──│FIRST_GOSSIP_PORT (9000) │<─┐ -//! │FIRST_HTTP_PORT (8000) │ │ -//! └────────────────────────────┘ │ -//! │ -//! │ Peer -//! ┌────────────────────────────┐ │ -//! │Task 1 │ │ -//! ┌─>│FIRST_GOSSIP_PORT+1 (9001) │──┘ -//! │ │FIRST_HTTP_PORT+1 (8001) │ -//! │ └────────────────────────────┘ -//! │ -//! Peer │ -//! │ ┌────────────────────────────┐ -//! │ │Task 2 │ -//! └──│FIRST_GOSSIP_PORT+2 (9002) │ -//! │FIRST_HTTP_PORT+2 (8002) │ -//! └────────────────────────────┘ -//! - -#[macro_use] -extern crate habitat_director as director; -extern crate habitat_core as hcore; -extern crate habitat_common as hcommon; -#[macro_use] -extern crate habitat_sup; -#[macro_use] -extern crate clap; -extern crate env_logger; -#[macro_use] -extern crate log; - -use std::process; - -use hcommon::ui::UI; -use hcore::config::ConfigFile; -use hcore::fs::am_i_root; - -use director::config::Config; -use director::controller::Controller; -use director::task::ExecContext; -use director::error::{Error, Result}; - -const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/VERSION")); -const CFG_DEFAULT_PATH: &'static str = "/hab/svc/hab-director/config.toml"; - -static LOGKEY: &'static str = "DIR"; - -fn main() { - env_logger::init().unwrap(); - let matches = app().get_matches(); - debug!("CLI matches: {:?}", matches); - let config = match config_from_args(&matches) { - Ok(result) => result, - Err(e) => return exit_with(e, 1), - }; - match dispatch(config, &matches) { - Ok(_) => std::process::exit(0), - Err(e) => exit_with(e, 1), - } -} - -fn app<'a, 'b>() -> clap::App<'a, 'b> { - clap_app!(Director => - (version: VERSION) - (about: "Launch and supervise multiple Habitat services") - (@setting VersionlessSubcommands) - (@setting SubcommandRequiredElseHelp) - (@subcommand start => - (about: "Run a Habitat director") - (@arg config: -c --config +takes_value +global - "Path to configuration file. [default: config.toml]") - ) - ) -} - -fn config_from_args(matches: &clap::ArgMatches) -> Result { - let cmd = matches.subcommand_name().unwrap(); - let args = matches.subcommand_matches(cmd).unwrap(); - - let config = match args.value_of("config") { - Some(cfg_path) => try!(Config::from_file(cfg_path)), - None => Config::from_file(CFG_DEFAULT_PATH).unwrap_or(Config::default()), - }; - Ok(config) -} - -fn dispatch(config: Config, matches: &clap::ArgMatches) -> Result<()> { - match matches.subcommand_name() { - Some("start") => start(config), - Some(cmd) => { - debug!("Dispatch failed, no match for command: {:?}", cmd); - Ok(()) - } - None => Ok(()), - } -} - -fn start(config: Config) -> Result<()> { - let mut ui = UI::default(); - if !am_i_root() { - try!(ui.warn("Running the Habitat Supervisor requires root or administrator privileges. \ - Please retry this command as a super user or use a privilege-granting \ - facility such as sudo.")); - try!(ui.br()); - return Err(Error::RootRequired); - } - - outputln!("Starting Controller"); - let ec = ExecContext::default(); - let mut controller = Controller::new(config, ec); - try!(controller.start()); - Ok(()) -} - -fn exit_with(err: Error, code: i32) { - println!("{}", err); - process::exit(code) -} diff --git a/components/director/src/task.rs b/components/director/src/task.rs deleted file mode 100644 index 886c3b3933..0000000000 --- a/components/director/src/task.rs +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fs::{self, File}; -use std::io::BufReader; -use std::io::prelude::*; -use std::net::SocketAddrV4; -use std::path::PathBuf; -use std::process::{Command, Stdio, Child}; -use std::str::FromStr; -use std::thread; - -use libc; -use time::{Duration, SteadyTime}; - -use error::Result; -use hcore; -use hcore::package::PackageIdent; -use hsup::package::Package; -use hsup::manager::signals; -use super::ServiceDef; -use super::error::Error; - -const HAB_SUP_PATH: &'static str = "/src/components/sup/target/debug/hab-sup"; -static LOGKEY: &'static str = "TASK"; - -// Functions from POSIX libc. -extern "C" { - fn waitpid(pid: libc::pid_t, status: *mut libc::c_int, options: libc::c_int) -> libc::pid_t; -} - -/// Where and with what command a Task runs -/// This is most useful for testing. -#[derive(Debug, Clone)] -pub struct ExecContext { - pub sup_path: PathBuf, - pub service_root: PathBuf, -} - -impl ExecContext { - pub fn new(sup_path: PathBuf, service_root: PathBuf) -> ExecContext { - ExecContext { - sup_path: sup_path, - service_root: service_root, - } - } -} - - -impl Default for ExecContext { - /// use the latest hab-sup and the default Hab service root - /// Fall back to HAB_SUP_PATH if we can't find it. - /// Run hab-director with `RUST_LOG=habitat_director=debug` - /// to see the start params including hab-sup path - fn default() -> ExecContext { - if let Ok(ident) = PackageIdent::from_str("core/hab-sup") { - if let Ok(pkg) = Package::load(&ident, None) { - let mut hab_director = pkg.path().to_path_buf(); - hab_director.push("bin"); - hab_director.push("hab-sup"); - ExecContext::new(hab_director, hcore::fs::svc_root()) - } else { - println!("Can't find core/hab-sup, falling back to {}", &HAB_SUP_PATH); - ExecContext::new(PathBuf::from(HAB_SUP_PATH), hcore::fs::svc_root()) - } - } else { - println!("Can't find core/hab-sup, falling back to {}", &HAB_SUP_PATH); - ExecContext::new(PathBuf::from(HAB_SUP_PATH), hcore::fs::svc_root()) - } - } -} - -/// Values for a Task that are generated by the Controller. -/// These values will be unique per Task. -#[derive(Debug, Clone)] -pub struct ExecParams { - pub gossip_listen: SocketAddrV4, - pub sidecar_listen: SocketAddrV4, - pub initial_peer: Option, -} - - -impl ExecParams { - pub fn new(gossip_listen: SocketAddrV4, - sidecar_listen: SocketAddrV4, - initial_peer: Option) - -> ExecParams { - ExecParams { - gossip_listen: gossip_listen, - sidecar_listen: sidecar_listen, - initial_peer: initial_peer, - } - } -} - - -/// A Task watches a child hab-sup process (a "supervisor supervisor"). -/// It knows how to generate CLI args for `hab-sup start` based on -/// it's ServiceDef, ExecContext, and ExecParams. -#[derive(Debug)] -pub struct Task { - pub pid: Option, - pub service_def: ServiceDef, - pub exec_ctx: ExecContext, - pub exec_params: ExecParams, - pub state_entered: SteadyTime, - pub starts: u64, -} - -impl Task { - pub fn new(exec_ctx: ExecContext, exec_params: ExecParams, service_def: ServiceDef) -> Task { - Task { - pid: None, - service_def: service_def, - exec_ctx: exec_ctx, - exec_params: exec_params, - state_entered: SteadyTime::now(), - starts: 0, - } - } - - pub fn status(&self) -> String { - let desc = if self.is_up() { "Started" } else { "Stopped" }; - format!("{}: {} for {}", - self.service_def.to_string(), - desc, - SteadyTime::now() - self.state_entered) - } - - pub fn get_cmd_args(&self) -> Result> { - let mut args = match self.service_def.cli_args.as_ref() { - Some(s) => s.as_str().split(" ").map(|s| s.to_string()).collect::>(), - None => Vec::new(), - }; - - args.insert(0, "start".to_string()); - args.insert(1, self.service_def.ident.to_string()); - args.push("--listen-gossip".to_string()); - args.push(self.exec_params.gossip_listen.to_string()); - args.push("--listen-http".to_string()); - args.push(self.exec_params.sidecar_listen.to_string()); - args.push("--group".to_string()); - args.push(self.service_def.service_group.group.clone()); - if let Some(org) = self.service_def.service_group.organization.as_ref() { - args.push("--org".to_string()); - args.push(org.clone()); - } - if let Some(initial_peer) = self.exec_params.initial_peer { - args.push("--peer".to_string()); - args.push(initial_peer.to_string()); - } - - // remove whitespace, it messes up the Command args - args.retain(|s| s != ""); - for arg in &args { - debug!("ARG [{}]", &arg); - } - Ok(args) - } - - pub fn start(&mut self) -> Result<()> { - if self.pid.is_none() { - let args = try!(self.get_cmd_args()); - - debug!("hab-up exe = [{}]", - &self.exec_ctx.sup_path.to_string_lossy()); - - outputln!("Starting {} [gossip {}, http API: {}, peer: {}]", - &self.service_def.to_string(), - &self.exec_params.gossip_listen, - &self.exec_params.sidecar_listen, - &self.exec_params.initial_peer - .as_ref() - .map_or("None".to_string(), |v| v.to_string()), - ); - - let mut cmd = Command::new(&self.exec_ctx.sup_path); - // rebind to increase lifetime and make the compiler happy - let mut cmd = cmd.args(&args) - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - - for (k, v) in &self.service_def.env { - cmd.env(&k, &v); - debug!("ENV {}={}", &k, &v); - } - - let mut child = try!(cmd.spawn()); - self.pid = Some(child.id()); - - outputln!("Started {} [gossip {}, http API: {}, peer: {}, pid: {}]", - &self.service_def.to_string(), - &self.exec_params.gossip_listen, - &self.exec_params.sidecar_listen, - &self.exec_params - .initial_peer - .as_ref() - .map_or("None".to_string(), |v| v.to_string()), - &child.id()); - - try!(self.transition_to_started()); - let name = self.service_def.to_string(); - try!(thread::Builder::new() - .name(String::from(name.clone())) - .spawn(move || -> Result<()> { child_reader(&mut child, name) })); - debug!("Spawned child reader"); - } else { - outputln!("{} already started", &self.service_def.to_string()); - } - Ok(()) - } - - pub fn is_up(&self) -> bool { - self.pid.is_some() - } - - pub fn is_down(&self) -> bool { - self.pid.is_none() - } - - // if the child process exists, check it's status via waitpid(). - // Returns true if the process is still running, false if it has died. - pub fn check_process(&mut self) -> Result<()> { - if self.pid.is_none() { - return Ok(()); - } - - unsafe { - let mut status: libc::c_int = 0; - let cpid = self.pid.unwrap() as libc::pid_t; - match waitpid(cpid, &mut status, 1 as libc::c_int) { - 0 => {} // Nothing returned, - pid if pid == cpid => { - if libc::WIFEXITED(status) { - let exit_code = libc::WEXITSTATUS(status); - outputln!("{} - process {} died with exit code {}", - self.service_def.ident.name, - pid, - exit_code); - } else if libc::WIFSIGNALED(status) { - let exit_signal = libc::WTERMSIG(status); - outputln!("{} - process {} died with signal {}", - self.service_def.ident.name, - pid, - exit_signal); - } else { - outputln!("{} - process {} died, but I don't know how.", - self.service_def.ident.name, - pid); - } - try!(self.transition_to_stopped()); - outputln!("{} - Service exited", self.service_def.ident.name); - } - // ZOMBIES! Bad zombies! We listen for zombies. ZOMBOCOM! - pid => { - if libc::WIFEXITED(status) { - let exit_code = libc::WEXITSTATUS(status); - debug!("Process {} died with exit code {}", pid, exit_code); - } else if libc::WIFSIGNALED(status) { - let exit_signal = libc::WTERMSIG(status); - debug!("Process {} terminated with signal {}", pid, exit_signal); - } else { - debug!("Process {} died, but I don't know how.", pid); - } - } - } - } - Ok(()) - } - - fn transition_to_stopped(&mut self) -> Result<()> { - self.pid = None; - self.cleanup_pidfile(); - self.state_entered = SteadyTime::now(); - Ok(()) - } - - fn transition_to_started(&mut self) -> Result<()> { - try!(self.create_pidfile()); - self.starts += 1; - self.state_entered = SteadyTime::now(); - Ok(()) - } - - pub fn service_dir(&self) -> PathBuf { - PathBuf::from(&self.exec_ctx.service_root).join("hab-director") - } - - pub fn pid_file(&self) -> PathBuf { - let sd = &self.service_def.to_string().replace(".", "-"); - let filename = format!("{}.pid", sd); - self.service_dir().join("data").join(filename) - } - - // 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) -> Result<()> { - match self.pid { - Some(ref pid) => { - let pid_file = self.pid_file(); - debug!("Creating PID file for child {} -> {:?}", - pid_file.display(), - pid); - if let Some(parent) = pid_file.parent() { - if let Err(e) = fs::create_dir_all(parent) { - // in most cases, the directory already exists - debug!("Couldn't make pid directory: {}", e); - } - } - let mut f = try!(File::create(pid_file)); - try!(write!(f, "{}", pid)); - Ok(()) - } - None => 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) { - let pid_file = self.pid_file(); - debug!("Attempting to clean up pid file {}", &pid_file.display()); - match fs::remove_file(pid_file) { - Ok(_) => { - debug!("Removed pid file"); - } - Err(e) => { - debug!("Error removing pidfile: {}, continuing", e); - } - }; - } - - // 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) -> Result> { - let pid_file = self.pid_file(); - debug!("Reading pidfile {}", &pid_file.display()); - 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::() { - Ok(pid) => pid, - Err(e) => { - debug!("Error reading pidfile: {}", e); - return Err(Error::DirectorError("Invalid pid file".to_string())); - } - }; - Ok(Some(pid)) - } - - - - /// Send a SIGTERM to a process, wait 8 seconds, then send SIGKILL - /// shamelessly copied and tweaked from supervisor.rs - pub fn stop(&mut self) -> Result<()> { - let wait = match self.pid { - Some(ref pid) => { - outputln!("Stopping {}", self.service_def.to_string()); - let _ = signals::send_signal(*pid, signals::Signal::SIGTERM as u32); - true - } - None => { - outputln!("{} already stopped", self.service_def.to_string()); - false - } - }; - if wait { - let stop_time = SteadyTime::now() + Duration::seconds(8); - loop { - try!(self.check_process()); - if SteadyTime::now() > stop_time { - outputln!("{} process failed to stop with SIGTERM; sending SIGKILL", - self.service_def.to_string()); - if let Some(pid) = self.pid { - let _ = signals::send_signal(pid, signals::Signal::SIGKILL as u32); - } - break; - } - if self.pid.is_none() { - break; - } else { - continue; - } - } - } - Ok(()) - } -} - -impl Drop for Task { - fn drop(&mut self) { - outputln!("Killing task {}", &self.service_def.to_string()); - let _ = self.stop(); - let _ = self.cleanup_pidfile(); - } -} - -// Consume output from a child process until EOF, then finish -fn child_reader(child: &mut Child, child_name: String) -> Result<()> { - debug!("Started reader for {}", &child_name); - let c_stdout = match child.stdout { - Some(ref mut s) => s, - None => return Err(Error::DirectorError(format!("Can't read {} stdout", &child_name))), - }; - - let mut reader = BufReader::new(c_stdout); - let mut buffer = String::new(); - - while reader.read_line(&mut buffer).unwrap() > 0 { - let mut line = output_format!(preamble &child_name, logkey "O"); - line.push_str(&buffer); - print!("{}", line); - buffer.clear(); - } - debug!("child_reader exiting"); - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::net::SocketAddrV4; - use std::path::PathBuf; - use std::str::FromStr; - - use super::super::ServiceDef; - use super::*; - - fn get_test_dc() -> Task { - let mut sd = ServiceDef::from_str("core.redis.somegroup.someorg").unwrap(); - sd.cli_args = Some("-v -foo=bar".to_string()); - let exec_ctx = ExecContext::default(); - let exec_params = ExecParams::new(SocketAddrV4::from_str("127.0.0.1:9000").unwrap(), - SocketAddrV4::from_str("127.0.0.1:8000").unwrap(), - None); - Task::new(exec_ctx, exec_params, sd) - } - - /// parse args, inject listen-gossip and listen-http, no peer - #[test] - fn cmd_args_parsing_no_peer() { - let dc = get_test_dc(); - let args = dc.get_cmd_args().unwrap(); - // core/redis is specified in get_test_dc() - assert!(args.as_slice() == - ["start", - "core/redis", - "-v", - "-foo=bar", - "--listen-gossip", - "127.0.0.1:9000", - "--listen-http", - "127.0.0.1:8000", - "--group", - "somegroup", - "--org", - "someorg"]); - } - - /// parse args, inject listen-gossip, listen-http, peer - #[test] - fn cmd_args_parsing_peer() { - let mut dc = get_test_dc(); - let peer = SocketAddrV4::from_str("127.0.0.1:9876").unwrap(); - // override the test default peer - dc.exec_params.initial_peer = Some(peer); - let args = dc.get_cmd_args().unwrap(); - // core / redis is specified in get_test_dc() - assert!(args.as_slice() == - ["start", - "core/redis", - "-v", - "-foo=bar", - "--listen-gossip", - "127.0.0.1:9000", - "--listen-http", - "127.0.0.1:8000", - "--group", - "somegroup", - "--org", - "someorg", - "--peer", - "127.0.0.1:9876"]); - } - - /// test the pid filename using the default service directory - #[test] - fn pid_file_name_from_default_exec_ctx() { - let dc = get_test_dc(); - let path = dc.pid_file().clone(); - let path = path.to_str().unwrap(); - assert!("/hab/svc/hab-director/data/core-redis-somegroup-someorg.pid" == path); - } - - // test the pid filename using a custom service directory - #[test] - fn pid_file_name_from_custom_exec_ctx() { - let mut dc = get_test_dc(); - dc.exec_ctx.service_root = PathBuf::from("/tmp"); - let path = dc.pid_file().clone(); - let path = path.to_str().unwrap(); - println!("[{}]", path); - assert!("/tmp/hab-director/data/core-redis-somegroup-someorg.pid" == path); - } -} diff --git a/components/director/tests/functional.rs b/components/director/tests/functional.rs deleted file mode 100644 index 8067de9f89..0000000000 --- a/components/director/tests/functional.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate habitat_director as director; -extern crate habitat_core as hcore; -extern crate tempdir; -extern crate time; - -#[cfg(feature = "functional")] -pub mod director_tests { - use std::net::SocketAddrV4; - use std::path::PathBuf; - use std::str::FromStr; - - use tempdir::TempDir; - use time; - - use director::*; - use director::task::{Task, ExecContext, ExecParams}; - - // call a closure in a loop until it returns true - // or timeout after 30 seconds and return false - pub fn wait_until_true(dc: &mut Task, some_fn: F) -> bool - where F: Fn(&mut Task) -> bool - { - let wait_duration = time::Duration::seconds(30); - let current_time = time::now_utc().to_timespec(); - let stop_time = current_time + wait_duration; - while time::now_utc().to_timespec() < stop_time { - if some_fn(dc) { - return true; - } - } - false - } - - // Create a Task for testing - fn get_test_dc(name: &str) -> (PathBuf, Task) { - let tmp_service_path = TempDir::new(name).unwrap(); - let mut sd = ServiceDef::from_str("core.functional_test.somegroup.someorg").unwrap(); - sd.cli_args = Some("-v".to_string()); - let mut exec_ctx = ExecContext::default(); - exec_ctx.sup_path = PathBuf::from("/bin/bash"); - let tsp = tmp_service_path.into_path(); - let tsp2 = tsp.clone(); - exec_ctx.service_root = PathBuf::from(tsp); - - let exec_params = ExecParams::new(SocketAddrV4::from_str("127.0.0.1:9000").unwrap(), - SocketAddrV4::from_str("127.0.0.1:8000").unwrap(), - None); - - (tsp2, Task::new(exec_ctx, exec_params, sd)) - } - - /// This test starts a Task process using `bash -c sleep 1` - /// as it's executable. This allows me to check and see if the states - /// are correct before starting, when stopped, and when restarted. - /// The ExecContext allows us to change the name of the hab-sup - /// binary and the path to the service directory. - #[test] - fn task_state_test() { - - let (_tmp, mut dc) = get_test_dc("first"); - dc.service_def.cli_args = Some("-c sleep 1".to_string()); - - // check unstarted state - assert_eq!(None, dc.pid); - assert_eq!(0, dc.starts); - assert!(dc.is_down()); - - // check started state - dc.start().unwrap(); - assert_eq!(1, dc.starts); - assert!(dc.pid.is_some()); - assert!(dc.is_up()); - assert!(dc.pid_file().is_file()); - // does the contents of the pidfile match dc.pid? - assert_eq!(dc.pid.unwrap(), dc.read_pidfile().unwrap().unwrap()); - - assert!(wait_until_true(&mut dc, |d| { - d.check_process().unwrap(); - d.pid.is_none() && d.is_down() - })); - - // pidfile shouldn't exist anymore - assert!(!dc.pid_file().is_file()); - - // make sure we can start it again - dc.start().unwrap(); - assert_eq!(2, dc.starts); - assert!(dc.pid.is_some()); - assert!(dc.is_up()); - assert!(dc.pid_file().is_file()); - // does the contents of the pidfile match dc.pid? - assert_eq!(dc.pid.unwrap(), dc.read_pidfile().unwrap().unwrap()); - - assert!(wait_until_true(&mut dc, |d| { - d.check_process().unwrap(); - d.pid.is_none() && d.is_down() - })); - - // pidfile shouldn't exist anymore - assert!(!dc.pid_file().is_file()); - } -} diff --git a/terraform/builder-gateways.tf b/terraform/builder-gateways.tf index c1a40dd3ed..f19ccb6b1d 100644 --- a/terraform/builder-gateways.tf +++ b/terraform/builder-gateways.tf @@ -37,25 +37,6 @@ resource "aws_instance" "builder_api" { script = "${path.module}/scripts/bootstrap.sh" } - provisioner "file" { - source = "${path.module}/files/hab-director.service" - destination = "/home/ubuntu/hab-director.service" - } - - provisioner "remote-exec" { - inline = [ - "sudo mv /home/ubuntu/hab-director.service /etc/systemd/system/hab-director.service", - "sudo mkdir -p /hab/etc/director", - "cat < /tmp/director-config.toml", - "${data.template_file.gateway_director.rendered}", - "BODY", - "sudo mv /tmp/director-config.toml /hab/etc/director/config.toml", - "sudo systemctl daemon-reload", - "sudo systemctl start hab-director", - "sudo systemctl enable hab-director", - ] - } - tags { Name = "builder-api-${count.index}" X-Contact = "The Habitat Maintainers " @@ -64,17 +45,6 @@ resource "aws_instance" "builder_api" { } } -data "template_file" "gateway_director" { - template = "${file("${path.module}/templates/gateway-director.toml")}" - - vars { - env = "${var.env}" - - // peer_ip = "${aws_instance.router.0.private_ip}" - peer_ip = "${aws_instance.monolith.0.private_ip}" - } -} - resource "aws_security_group" "admin_gateway" { name = "builder-admin-gateway-${var.env}" vpc_id = "${var.aws_vpc_id}" diff --git a/terraform/builder-monolith.tf b/terraform/builder-monolith.tf index 6d3a4c2fd9..54914ca99d 100644 --- a/terraform/builder-monolith.tf +++ b/terraform/builder-monolith.tf @@ -42,25 +42,6 @@ resource "aws_instance" "monolith" { script = "${path.module}/scripts/bootstrap.sh" } - provisioner "file" { - source = "${path.module}/files/hab-director.service" - destination = "/home/ubuntu/hab-director.service" - } - - provisioner "remote-exec" { - inline = [ - "sudo mv /home/ubuntu/hab-director.service /etc/systemd/system/hab-director.service", - "sudo mkdir -p /hab/etc/director", - "cat < /tmp/director-config.toml", - "${data.template_file.monolith_director.rendered}", - "BODY", - "sudo mv /tmp/director-config.toml /hab/etc/director/config.toml", - "sudo systemctl daemon-reload", - "sudo systemctl start hab-director", - "sudo systemctl enable hab-director", - ] - } - tags { Name = "builder-monolith-${count.index}" X-Contact = "The Habitat Maintainers " @@ -68,11 +49,3 @@ resource "aws_instance" "monolith" { X-Application = "builder" } } - -data "template_file" "monolith_director" { - template = "${file("${path.module}/templates/monolith-director.toml")}" - - vars { - env = "${var.env}" - } -} diff --git a/terraform/builder-routers.tf b/terraform/builder-routers.tf index bba44d54c8..53a415e023 100644 --- a/terraform/builder-routers.tf +++ b/terraform/builder-routers.tf @@ -24,25 +24,6 @@ resource "aws_instance" "router" { script = "${path.module}/scripts/bootstrap.sh" } - provisioner "file" { - source = "${path.module}/files/hab-director.service" - destination = "/home/ubuntu/hab-director.service" - } - - provisioner "remote-exec" { - inline = [ - "sudo mv /home/ubuntu/hab-director.service /etc/systemd/system/hab-director.service", - "sudo mkdir -p /hab/etc/director", - "cat < /tmp/director-config.toml", - "${data.template_file.router_director.rendered}", - "BODY", - "sudo mv /tmp/director-config.toml /hab/etc/director/config.toml", - "sudo systemctl daemon-reload", - "sudo systemctl start hab-director", - "sudo systemctl enable hab-director", - ] - } - tags { Name = "builder-router-${count.index}" X-Contact = "The Habitat Maintainers " @@ -51,14 +32,6 @@ resource "aws_instance" "router" { } } -data "template_file" "router_director" { - template = "${file("${path.module}/templates/router-director.toml")}" - - vars { - env = "${var.env}" - } -} - resource "aws_security_group" "router_gateway" { name = "builder-router-gateway-${var.env}" vpc_id = "${var.aws_vpc_id}" diff --git a/terraform/builder-services.tf b/terraform/builder-services.tf index 87f3b3e2c9..5b6b65784f 100644 --- a/terraform/builder-services.tf +++ b/terraform/builder-services.tf @@ -37,25 +37,6 @@ resource "aws_instance" "services" { script = "${path.module}/scripts/bootstrap.sh" } - provisioner "file" { - source = "${path.module}/files/hab-director.service" - destination = "/home/ubuntu/hab-director.service" - } - - provisioner "remote-exec" { - inline = [ - "sudo mv /home/ubuntu/hab-director.service /etc/systemd/system/hab-director.service", - "sudo mkdir -p /hab/etc/director", - "cat < /tmp/director-config.toml", - "${data.template_file.services_director.rendered}", - "BODY", - "sudo mv /tmp/director-config.toml /hab/etc/director/config.toml", - "sudo systemctl daemon-reload", - "sudo systemctl start hab-director", - "sudo systemctl enable hab-director", - ] - } - tags { Name = "builder-service-${count.index}" X-Contact = "The Habitat Maintainers " @@ -64,17 +45,6 @@ resource "aws_instance" "services" { } } -data "template_file" "services_director" { - template = "${file("${path.module}/templates/services-director.toml")}" - - vars { - env = "${var.env}" - - // peer_ip = "${aws_instance.router.0.private_ip}" - peer_ip = "${aws_instance.monolith.0.private_ip}" - } -} - resource "aws_security_group" "service" { name = "builder-service-${var.env}" description = "Allow traffic to and from Habitat Builder service instance" diff --git a/terraform/builder-workers.tf b/terraform/builder-workers.tf index 9f232546a2..a69830d25d 100644 --- a/terraform/builder-workers.tf +++ b/terraform/builder-workers.tf @@ -25,25 +25,6 @@ resource "aws_instance" "jobsrv_workers" { script = "${path.module}/scripts/bootstrap.sh" } - provisioner "file" { - source = "${path.module}/files/hab-director.service" - destination = "/home/ubuntu/hab-director.service" - } - - provisioner "remote-exec" { - inline = [ - "sudo mv /home/ubuntu/hab-director.service /etc/systemd/system/hab-director.service", - "sudo mkdir -p /hab/etc/director", - "cat < /tmp/director-config.toml", - "${data.template_file.worker_director.rendered}", - "BODY", - "sudo mv /tmp/director-config.toml /hab/etc/director/config.toml", - "sudo systemctl daemon-reload", - "sudo systemctl start hab-director", - "sudo systemctl enable hab-director", - ] - } - tags { Name = "builder-worker-${count.index}" X-Contact = "The Habitat Maintainers " @@ -52,17 +33,6 @@ resource "aws_instance" "jobsrv_workers" { } } -data "template_file" "worker_director" { - template = "${file("${path.module}/templates/worker-director.toml")}" - - vars { - env = "${var.env}" - - // peer_ip = "${aws_instance.router.0.private_ip}" - peer_ip = "${aws_instance.monolith.0.private_ip}" - } -} - resource "aws_security_group" "worker" { name = "builder-worker-${var.env}" description = "Basic Traffic rules for worker instances" diff --git a/terraform/files/hab-director.service b/terraform/files/hab-director.service deleted file mode 100644 index deaa32007d..0000000000 --- a/terraform/files/hab-director.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=Habitat Director - -[Service] -ExecStartPre=/bin/bash -c "/bin/systemctl set-environment SSL_CERT_FILE=$(hab pkg path core/cacerts)/ssl/cert.pem" -ExecStart=/bin/hab-director start -c /hab/etc/director/config.toml - -[Install] -WantedBy=default.target diff --git a/terraform/scripts/bootstrap.sh b/terraform/scripts/bootstrap.sh index 06c35d73fc..7fe477438e 100644 --- a/terraform/scripts/bootstrap.sh +++ b/terraform/scripts/bootstrap.sh @@ -53,6 +53,3 @@ archive_dir="$(echo $archive | sed 's/.tar.gz$//')" sudo "$archive_dir/hab" install core/hab > /dev/null 2>&1 sudo "$archive_dir/hab" pkg binlink core/hab hab sudo hab install core/hab-sup > /dev/null 2>&1 -sudo hab install core/hab-director > /dev/null 2>&1 -# JW TODO: give director the same treatment as sup in hab -sudo hab pkg binlink core/hab-director hab-director diff --git a/terraform/templates/gateway-director.toml b/terraform/templates/gateway-director.toml deleted file mode 100644 index 2bf125e730..0000000000 --- a/terraform/templates/gateway-director.toml +++ /dev/null @@ -1,8 +0,0 @@ -[cfg.services.core.redis.${env}] -start = "--permanent-peer" - -[cfg.services.core.hab-builder-api.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core/hab-builder-admin.${env}] -start = "--permanent-peer --bind router:hab-builder-router.${env}" diff --git a/terraform/templates/monolith-director.toml b/terraform/templates/monolith-director.toml deleted file mode 100644 index e1c6fb4db6..0000000000 --- a/terraform/templates/monolith-director.toml +++ /dev/null @@ -1,29 +0,0 @@ -[cfg.services.core.redis.${env}] -start = "--permanent-peer" - -[cfg.services.core.hab-builder-router.${env}] -start = "--permanent-peer" - -[cfg.services.core.hab-builder-jobsrv.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.hab-builder-sessionsrv.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.hab-builder-vault.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.hab-builder-worker.${env}] -start = "--permanent-peer --bind jobsrv::hab-builder-jobsrv.${env}" - -[cfg.services.core.hab-builder-api.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.builder-api-proxy.${env}] -start = "--permanent-peer --bind api:hab-builder-api.${env}" - -[cfg.services.core.hab-builder-admin.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.builder-admin-proxy.${env}] -start = "--permanent-peer --bind admin:hab-builder-admin.${env}" diff --git a/terraform/templates/router-director.toml b/terraform/templates/router-director.toml deleted file mode 100644 index f4960efc6f..0000000000 --- a/terraform/templates/router-director.toml +++ /dev/null @@ -1,2 +0,0 @@ -[cfg.services.core.hab-builder-router.${env}] -start = "--permanent-peer" diff --git a/terraform/templates/services-director.toml b/terraform/templates/services-director.toml deleted file mode 100644 index 14659a1d38..0000000000 --- a/terraform/templates/services-director.toml +++ /dev/null @@ -1,11 +0,0 @@ -[cfg.services.core.redis.${env}] -start = "--permanent-peer" - -[cfg.services.core.hab-builder-jobsrv.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.hab-builder-sessionsrv.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" - -[cfg.services.core.hab-builder-vault.${env}] -start = "--permanent-peer --bind database:redis.${env} --bind router:hab-builder-router.${env}" diff --git a/terraform/templates/worker-director.toml b/terraform/templates/worker-director.toml deleted file mode 100644 index c184202477..0000000000 --- a/terraform/templates/worker-director.toml +++ /dev/null @@ -1,2 +0,0 @@ -[cfg.services.core.hab-builder-worker.${env}] -start = "--permanent-peer --bind jobsrv:hab-builder-jobsrv.${env}" diff --git a/www/data/docs_sidebar.yml b/www/data/docs_sidebar.yml index e28e561e6a..92858ba5f4 100644 --- a/www/data/docs_sidebar.yml +++ b/www/data/docs_sidebar.yml @@ -13,8 +13,6 @@ sidebar_links: sub_links: - title: Supervisor link: "/docs/concepts-supervisor/" - - title: Director - link: "/docs/concepts-director" - title: Plans link: "/docs/concepts-plans/" - title: Packages @@ -59,8 +57,6 @@ sidebar_links: link: "/docs/run-packages-binding/" - title: Update strategy link: "/docs/run-packages-update-strategy/" - - title: Run multiple packages - link: "/docs/run-packages-director/" - title: Export packages link: "/docs/run-packages-export/" - title: Monitoring diff --git a/www/source/docs/concepts-director.html.md b/www/source/docs/concepts-director.html.md deleted file mode 100644 index f918355788..0000000000 --- a/www/source/docs/concepts-director.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Director ---- - -# Director - -The Habitat director is a supervisor for a group of supervisor (`hab-sup`) processes running on one machine. The director automatically restarts child processes upon failure detection and each child service runs in it's own supervisor process. - -Services are listed a config.toml file. This file defines the start order, service group, organization, and any CLI arguments for each service. - - > Note: The start order does not guarantee processes are fully initialized and started before the next one in the list is started. - -The director can be run inside of a supervisor as well. As with any other service, this allows the director to be updated with new configuration changes at runtime, which enable it to dynamically deploy different child service configurations and topologies. - -When running inside of a supervisor, the director will use the standard gossip and HTTP API ports of 9638 and 9631, respectively. However, child supervisor processes will use the same IP address of the director. The default for every service is to use ports 9638 and 9631, so to stop port collision from happening when the child `hab-sup` processes start up, the director defines ring and HTTP API port numbers for all children in sequential order starting with port 9000 for gossip connections and port 8000 for HTTP API connections. It's advisable to keep the director's port numbers at their default values to avoid port collisions. - -
-
    -
  • Continue to the next topic
  • -
  • Plans
  • -
diff --git a/www/source/docs/concepts-supervisor.html.md b/www/source/docs/concepts-supervisor.html.md index fffd14e565..14953c52e4 100644 --- a/www/source/docs/concepts-supervisor.html.md +++ b/www/source/docs/concepts-supervisor.html.md @@ -25,5 +25,5 @@ The Habitat supervisor provides a HTTP API to expose cluster metadata, statistic
diff --git a/www/source/docs/run-packages-director.html.md b/www/source/docs/run-packages-director.html.md deleted file mode 100644 index 50154f3de5..0000000000 --- a/www/source/docs/run-packages-director.html.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: Run multiple packages using the director ---- - -# Run multiple packages using the director -The director is a supervisor that can quickly start up and manage multiple services on _one machine_ using a config.toml file, and when run in a supervisor process (`hab-sup`) itself, the director can be reconfigured at runtime like any other Habitat service. Reconfiguring the director causes all child processes to be restarted. - -## Defining the services - -The config.toml file used by the director contains one or more service definitions. Service definitions are combination of a package identifier, service group, and CLI arguments. They are specified as a dot-separated list in a [TOML table](https://github.com/toml-lang/toml#table) name. - - [services....] - -The following example corresponds to the `core/redis` package in the redis.somegroup service group. - - [services.core.redis.somegroup] - -This example corresponds to the `core/redis` package in the redis.somegroup service group which is in the someorg organization. - - [services.core.redis.somegroup.someorg] - -> Note: All services must be described as children of the services TOML table. When the TOML is rendered, the values for services will be -located under `cfg.services.*` . - -A service definition can additionally specify a start key/value under -the service table definition: - - # Start core/redis with --group somegroup and --org someorg - # Additionally, pass in --permanent-peer to the start command - [services.core.redis.somegroup.someorg] - start = "--permanent-peer" - - [services.core.rngd.foo.someorg] - start = "--permanent-peer --foo=bar" - -> Note: CLI arguments specified in config.toml are split on whitespace. - - -Services can provide environment variables in the form of a TOML table which follows the following format: - - [services.....env] - ENV1="some value" - ENV2="some other value" - -> Note: Environment variables MUST be specified as valid TOML strings. - -For example: - - # Specify custom JAVA_HOME and CLASSPATH environment variables - [services.core.java_app.somegroup.someorg] - start = "--permanent-peer" - [services.core.java_app.somegroup.someorg.env] - JAVA_HOME="/some/path/" - CLASSPATH="/some/classpath/foo.jar" - - [services.core.rngd.foo.someorg] - start = "--permanent-peer --foo=bar" - [services.core.rngd.foo.someorg.env] - JAVA_HOME="/a/different/path/" - # we don't specify CLASSPATH here, so it won't be set for core/rngd - -> Note: We current don't support global environment variables. - -## Using the director -When run in a supervisor, the director can be started using the `hab start` command. - - hab start core/hab-director - -You must also pass in the config.toml file containing your service definitions. This can be done at runtime dynamically by using the `hab config apply` subcommand. - - hab config apply hab-director.default --peer 172.17.0.2 1 /path/to/config.toml - -
- diff --git a/www/source/docs/run-packages-overview.html.md.erb b/www/source/docs/run-packages-overview.html.md.erb index 0a8608df96..8235347242 100644 --- a/www/source/docs/run-packages-overview.html.md.erb +++ b/www/source/docs/run-packages-overview.html.md.erb @@ -49,7 +49,6 @@ This section is divided into the following areas: - [Security](/docs/run-packages-security): Describes how to encrypt communication between supervisors, and between users and service groups. - [Binding](/docs/run-packages-binding): Learn how to bind to unknown service group names at runtime. - [Update strategy](/docs/run-packages-update-strategy): Describes how the supervisor and its peers within a service group should respond when a new version of a package is available. -- [Run multiple packages](/docs/run-packages-director): Learn about the director and how to use it to start up multiple services on the same machine. - [Export packages](/docs/run-packages-export): Learn how to export packages into multiple external, immutable runtime formats and work with container cluster managers like Kubernetes and Mesos. - [Monitor services through the HTTP API](/docs/run-packages-monitoring): Discover how to use the HTTP API to retrieve census, status, and health information from your running services. diff --git a/www/source/docs/run-packages-update-strategy.html.md b/www/source/docs/run-packages-update-strategy.html.md index ff69bf6daa..1487874507 100644 --- a/www/source/docs/run-packages-update-strategy.html.md +++ b/www/source/docs/run-packages-update-strategy.html.md @@ -52,5 +52,5 @@ _At the moment, the `hab` command-line tool lacks the ability to create and mana