Skip to content

Commit

Permalink
Merge pull request #1093 from habitat-sh/fnichol/smart-install
Browse files Browse the repository at this point in the history
Rewrite package install strategy.
  • Loading branch information
bookshelfdave committed Aug 6, 2016
2 parents 35ebdac + 2e19a32 commit 9d73677
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 261 deletions.
380 changes: 191 additions & 189 deletions components/common/src/command/package/install.rs

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions components/common/src/error.rs
Expand Up @@ -27,6 +27,7 @@ pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
ArtifactIdentMismatch((String, String, String)),
CantUploadGossipToml,
CryptoKeyError(String),
GossipFileRelativePath(String),
Expand All @@ -46,6 +47,12 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = match *self {
Error::ArtifactIdentMismatch((ref a, ref ai, ref i)) => {
format!("Artifact ident {} for `{}' does not match expected ident {}",
ai,
a,
i)
}
Error::CantUploadGossipToml => {
format!("Can't upload gossip.toml, it's a reserved file name")
}
Expand All @@ -72,6 +79,9 @@ impl fmt::Display for Error {
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::ArtifactIdentMismatch((_, _, _)) => {
"Artifact ident does not match expected ident"
}
Error::CantUploadGossipToml => "Can't upload gossip.toml, it's a reserved filename",
Error::CryptoKeyError(_) => "Missing or invalid key",
Error::GossipFileRelativePath(_) => {
Expand Down
14 changes: 7 additions & 7 deletions components/hab/src/command/pkg.rs
Expand Up @@ -219,13 +219,13 @@ pub mod export {
println!("Searching for {} in remote {}",
&format_ident.to_string(),
&default_depot_url());
try!(install::from_url(&default_depot_url(),
format_ident,
PRODUCT,
VERSION,
Path::new(FS_ROOT_PATH),
&cache_artifact_path(None),
&default_cache_key_path(None)));
try!(install::start(&default_depot_url(),
&format_ident.to_string(),
PRODUCT,
VERSION,
Path::new(FS_ROOT_PATH),
&cache_artifact_path(None),
&default_cache_key_path(None)));
}
}
let pkg_arg = OsString::from(&ident.to_string());
Expand Down
14 changes: 7 additions & 7 deletions components/hab/src/exec.rs
Expand Up @@ -98,13 +98,13 @@ pub fn command_from_pkg(command: &str,
println!("{}",
Cyan.bold()
.paint(format!("∵ Package for {} not found, installing", &ident)));
try!(common::command::package::install::from_url(&default_depot_url(),
ident,
PRODUCT,
VERSION,
fs_root_path,
&cache_artifact_path(None),
cache_key_path));
try!(common::command::package::install::start(&default_depot_url(),
&ident.to_string(),
PRODUCT,
VERSION,
fs_root_path,
&cache_artifact_path(None),
cache_key_path));
command_from_pkg(&command, &ident, &cache_key_path, retry + 1)
}
Err(e) => return Err(Error::from(e)),
Expand Down
2 changes: 1 addition & 1 deletion components/hab/src/gossip.rs
Expand Up @@ -84,7 +84,7 @@ pub mod hab_gossip {
use common::gossip_file::GossipFile;
use common::wire_message::WireMessage;
use hcore::crypto::SymKey;
use rustc_serialize::{json, Encodable};
use rustc_serialize::json;
use utp::UtpSocket;
use uuid::Uuid;

Expand Down
93 changes: 51 additions & 42 deletions components/sup/src/command/start.rs
Expand Up @@ -60,15 +60,14 @@ use std::env;
use std::path::Path;

use ansi_term::Colour::Yellow;
use common::command::ProgressBar;
use common::command::package::install;
use depot_client::Client;
use hcore::crypto::default_cache_key_path;
use hcore::fs::{cache_artifact_path, FS_ROOT_PATH};
use hcore::package::PackageIdent;

use {PRODUCT, VERSION};
use error::{Error, Result};
use error::Result;
use config::{Config, UpdateStrategy};
use package::Package;
use topology::{self, Topology};
Expand All @@ -85,61 +84,71 @@ static LOGKEY: &'static str = "CS";
/// * Fails if an unknown topology was specified on the command line
pub fn package(config: &Config) -> Result<()> {
match Package::load(config.package(), None) {
Ok(package) => {
Ok(mut package) => {
let update_strategy = config.update_strategy();
match update_strategy {
UpdateStrategy::None => {}
_ => {
if let &Some(ref url) = config.url() {
outputln!("Checking remote for newer versions...");
// It is important to pass `config.package()` to `show_package()` instead of the
// package identifier of the loaded package. This will ensure that if the operator
// starts a package while specifying a version number, they will only automaticaly
// receive release updates for the started package.
//
// If the operator does not specify a version number they will automatically receive
// updates for any releases, regardless of version number, for the started package.
let depot_client = try!(Client::new(url, PRODUCT, VERSION, None));
let latest_pkg_data =
try!(depot_client.show_package((*config.package()).clone()));
let latest_ident: PackageIdent = latest_pkg_data.get_ident().clone().into();
if &latest_ident > package.ident() {
outputln!("Downloading latest version from remote: {}", latest_ident);
let mut progress = ProgressBar::default();
let archive = try!(depot_client.fetch_package(latest_ident,
&cache_artifact_path(None),
Some(&mut progress)));
try!(archive.verify(&default_cache_key_path(None)));
try!(archive.unpack(None));
} else {
outputln!("Already running latest.");
};
}
let url = config.url();
outputln!("Checking Depot for newer versions...");
// It is important to pass `config.package()` to `show_package()` instead
// of the package identifier of the loaded package. This will ensure that
// if the operator starts a package while specifying a version number, they
// will only automaticaly receive release updates for the started package.
//
// If the operator does not specify a version number they will
// automatically receive updates for any releases, regardless of version
// number, for the started package.
let depot_client = try!(Client::new(url, PRODUCT, VERSION, None));
let latest_pkg_data =
try!(depot_client.show_package((*config.package()).clone()));
let latest_ident: PackageIdent = latest_pkg_data.get_ident().clone().into();
if &latest_ident > package.ident() {
outputln!("Downloading latest version from Depot: {}", latest_ident);
let new_pkg_data = try!(install::start(url,
&latest_ident.to_string(),
PRODUCT,
VERSION,
Path::new(FS_ROOT_PATH),
&cache_artifact_path(None),
&default_cache_key_path(None)));
package = try!(Package::load(&new_pkg_data, None));
} else {
outputln!("Already running latest.");
};
}
}
start_package(package, config)
}
Err(_) => {
outputln!("{} is not installed",
Yellow.bold().paint(config.package().to_string()));
match *config.url() {
Some(ref url) => {
let url = config.url();
let new_pkg_data = match config.local_artifact() {
Some(artifact) => {
try!(install::start(url,
&artifact,
PRODUCT,
VERSION,
Path::new(FS_ROOT_PATH),
&cache_artifact_path(None),
&default_cache_key_path(None)))
}
None => {
outputln!("Searching for {} in remote {}",
Yellow.bold().paint(config.package().to_string()),
url);
let new_pkg_data = try!(install::from_url(url,
config.package(),
PRODUCT,
VERSION,
Path::new(FS_ROOT_PATH),
&cache_artifact_path(None),
&default_cache_key_path(None)));
let package = try!(Package::load(&new_pkg_data.get_ident().clone().into(),
None));
start_package(package, config)
try!(install::start(url,
&config.package().to_string(),
PRODUCT,
VERSION,
Path::new(FS_ROOT_PATH),
&cache_artifact_path(None),
&default_cache_key_path(None)))
}
None => Err(sup_error!(Error::PackageNotFound(config.package().clone()))),
}
};
let package = try!(Package::load(&new_pkg_data, None));
start_package(package, config)
}
}
}
Expand Down
18 changes: 14 additions & 4 deletions components/sup/src/config.rs
Expand Up @@ -85,7 +85,8 @@ impl Default for Command {
pub struct Config {
command: Command,
package: PackageIdent,
url: Option<String>,
local_artifact: Option<String>,
url: String,
topology: Topology,
group: String,
path: String,
Expand Down Expand Up @@ -261,12 +262,12 @@ impl Config {

/// Set the url
pub fn set_url(&mut self, url: String) -> &mut Config {
self.url = Some(url);
self.url = url;
self
}

/// Return the url
pub fn url(&self) -> &Option<String> {
pub fn url(&self) -> &str {
&self.url
}

Expand Down Expand Up @@ -382,6 +383,15 @@ impl Config {
&self.package
}

pub fn set_local_artifact(&mut self, artifact: String) -> &mut Config {
self.local_artifact = Some(artifact);
self
}

pub fn local_artifact(&self) -> Option<&str> {
self.local_artifact.as_ref().map(String::as_ref)
}

pub fn set_organization(&mut self, org: String) -> &mut Config {
self.organization = Some(org);
self
Expand Down Expand Up @@ -439,7 +449,7 @@ mod tests {
fn url() {
let mut c = Config::new();
c.set_url(String::from("http://foolio.com"));
assert_eq!(c.url().as_ref().unwrap(), "http://foolio.com");
assert_eq!(c.url(), "http://foolio.com");
}

#[test]
Expand Down
22 changes: 15 additions & 7 deletions components/sup/src/main.rs
Expand Up @@ -24,6 +24,7 @@ extern crate libc;
#[macro_use]
extern crate clap;

use std::path::Path;
use std::process;
use std::result;
use std::str::FromStr;
Expand All @@ -34,7 +35,7 @@ use hcore::env as henv;
use hcore::fs;
use hcore::crypto::{default_cache_key_path, SymKey};
use hcore::crypto::init as crypto_init;
use hcore::package::PackageIdent;
use hcore::package::{PackageArchive, PackageIdent};
use hcore::url::{DEFAULT_DEPOT_URL, DEPOT_URL_ENVVAR};

use sup::config::{Command, Config, UpdateStrategy};
Expand Down Expand Up @@ -74,9 +75,15 @@ fn config_from_args(subcommand: &str, sub_args: &ArgMatches) -> Result<Config> {
if let Some(ref archive) = sub_args.value_of("archive") {
config.set_archive(archive.to_string());
}
if let Some(ref package) = sub_args.value_of("package") {
let ident = try!(PackageIdent::from_str(package));
config.set_package(ident);
if let Some(ref ident_or_artifact) = sub_args.value_of("pkg_ident_or_artifact") {
if Path::new(ident_or_artifact).is_file() {
let ident = try!(PackageArchive::new(Path::new(ident_or_artifact)).ident());
config.set_package(ident);
config.set_local_artifact(ident_or_artifact.to_string());
} else {
let ident = try!(PackageIdent::from_str(ident_or_artifact));
config.set_package(ident);
}
}
if let Some(key) = sub_args.value_of("key") {
config.set_key(key.to_string());
Expand Down Expand Up @@ -255,12 +262,13 @@ fn main() {
};

let sub_start = SubCommand::with_name("start")
.about("Start a Habitat-supervised service from a package")
.about("Start a Habitat-supervised service from a package or artifact")
.aliases(&["st", "sta", "star"])
.arg(Arg::with_name("package")
.arg(Arg::with_name("pkg_ident_or_artifact")
.index(1)
.required(true)
.help("Name of package to start"))
.help("A Habitat package identifier (ex: acme/redis) or a filepath to a Habitat \
Artifact (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart)"))
.arg(arg_url())
.arg(arg_group())
.arg(arg_org())
Expand Down
4 changes: 1 addition & 3 deletions components/sup/src/topology/mod.rs
Expand Up @@ -148,9 +148,7 @@ impl<'a> Worker<'a> {
UpdateStrategy::None => {}
_ => {
let pkg_lock_2 = pkg_lock.clone();
if let &Some(ref url) = config.url() {
pkg_updater = Some(package::PackageUpdater::start(url, pkg_lock_2));
}
pkg_updater = Some(package::PackageUpdater::start(config.url(), pkg_lock_2));
}
}

Expand Down
1 change: 0 additions & 1 deletion components/sup/tests/util/command.rs
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::prelude::*;
use std::io;
use std::process::{Command, Child, Stdio, ExitStatus};
use std::fmt;
Expand Down

0 comments on commit 9d73677

Please sign in to comment.