diff --git a/Makefile b/Makefile index d4fb0b85f12..6860a17db1d 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,11 @@ gpg: ## install gpg keys, only run this in a studio (cd plans && make gpg) build: image ## run cargo build + $(run) shell cargo build --manifest-path components/core/Cargo.toml $(run) shell cargo build --manifest-path components/bldr/Cargo.toml + $(run) shell cargo build --manifest-path components/depot-core/Cargo.toml + $(run) shell cargo build --manifest-path components/depot/Cargo.toml + $(run) shell cargo build --manifest-path components/depot-client/Cargo.toml shell: image ## start a shell for building packages $(run) shell @@ -35,16 +39,32 @@ docs-serve: docs ## serve up the documentation $(run) -p 9633:9633 shell sh -c 'set -e; cd ./components/bldr/target/doc; python -m SimpleHTTPServer 9633;' test: image ## run `cargo test` + $(run) shell cargo test --manifest-path components/core/Cargo.toml $(run) shell cargo test --manifest-path components/bldr/Cargo.toml + $(run) shell cargo test --manifest-path components/depot-core/Cargo.toml + $(run) shell cargo test --manifest-path components/depot/Cargo.toml + $(run) shell cargo test --manifest-path components/depot-client/Cargo.toml unit: image ## run unit tests with cargo + $(run) shell cargo test --lib --manifest-path components/core/Cargo.toml $(run) shell cargo test --lib --manifest-path components/bldr/Cargo.toml + $(run) shell cargo test --lib --manifest-path components/depot-core/Cargo.toml + $(run) shell cargo test --lib --manifest-path components/depot/Cargo.toml + $(run) shell cargo test --lib --manifest-path components/depot-client/Cargo.toml functional: image ## run the functional tests + $(run) shell cargo test --test functional --manifest-path components/core/Cargo.toml $(run) shell cargo test --test functional --manifest-path components/bldr/Cargo.toml + $(run) shell cargo test --test functional --manifest-path components/depot-core/Cargo.toml + $(run) shell cargo test --test functional --manifest-path components/depot/Cargo.toml + $(run) shell cargo test --test functional --manifest-path components/depot-client/Cargo.toml clean: ## clean up our docker environment rm -rf components/bldr/target/debug components/bldr/target/release + rm -rf components/bldr/target/debug components/core/target/release + rm -rf components/bldr/target/debug components/depot-core/target/release + rm -rf components/bldr/target/debug components/depot/target/release + rm -rf components/bldr/target/debug components/depot-client/target/release $(compose_cmd) stop $(compose_cmd) rm -f -v $(docker_cmd) rmi $(dimage) || true diff --git a/components/bldr/Cargo.lock b/components/bldr/Cargo.lock index a793ed900a2..36559d31d5e 100644 --- a/components/bldr/Cargo.lock +++ b/components/bldr/Cargo.lock @@ -5,6 +5,9 @@ dependencies = [ "ansi_term 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bldr_core 0.4.0", + "bldr_depot_client 0.4.0", + "bldr_depot_core 0.4.0", "clap 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -79,6 +82,41 @@ name = "bitflags" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bldr_core" +version = "0.4.0" +dependencies = [ + "gpgme 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libarchive 0.1.0", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bldr_depot_client" +version = "0.4.0" +dependencies = [ + "bldr_core 0.4.0", + "bldr_depot_core 0.4.0", + "hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bldr_depot_core" +version = "0.4.0" +dependencies = [ + "bldr_core 0.4.0", + "hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lmdb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bodyparser" version = "0.2.0" diff --git a/components/bldr/Cargo.toml b/components/bldr/Cargo.toml index 65b1058e66b..4b954bab6ed 100644 --- a/components/bldr/Cargo.toml +++ b/components/bldr/Cargo.toml @@ -44,6 +44,15 @@ urlencoded = "*" openssl = "*" walkdir = "*" +[dependencies.bldr_core] +path = "../core" + +[dependencies.bldr_depot_core] +path = "../depot-core" + +[dependencies.bldr_depot_client] +path = "../depot-client" + [dependencies.wonder] path = "../../vendor/wonder" diff --git a/components/bldr/src/command/depot.rs b/components/bldr/src/command/depot.rs deleted file mode 100644 index 1448adde649..00000000000 --- a/components/bldr/src/command/depot.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. -// -// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing -// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software -// is made available under an open source license such as the Apache 2.0 License. - -//! Runs a bldr package depot. -//! -//! The depot is an HTTP service that runs on port `9632`. -//! -//! Look in the [depot](../../depot) module for more information on how the service itself operates. -//! -//! # Examples -//! -//! ```bash -//! $ bldr depot -//! ``` -//! -//! Starts a bldr depot, with the data stored in `/opt/bldr/srvc/bldr/data`. -//! -//! ```bash -//! $ bldr depot -p /tmp/whatever -//! ``` -//! -//! Does the same, but the data is stored in `/tmp/whatever`. - -use config::Config; -use error::{BldrError, BldrResult, ErrorKind}; -use depot::{self, data_object, doctor, Depot}; -use depot::data_store::{self, Cursor, Database, Transaction}; - -static LOGKEY: &'static str = "CR"; - -/// Create a repository with the given name in the depot. -/// -/// # Failures -/// -/// * The database cannot be read -/// * A write transaction cannot be acquired. -pub fn create_repository(name: &str, config: &Config) -> BldrResult<()> { - let depot = try!(Depot::new(String::from(config.path()))); - let txn = try!(depot.datastore.views.txn_rw()); - let object = data_object::View::new(name); - try!(depot.datastore.views.write(&txn, &object)); - Ok(()) -} - -/// List all repositories in the database. -/// -/// # Failures -/// -/// * The database cannot be read -/// * A read transaction cannot be acquired. -pub fn list_repositories(config: &Config) -> BldrResult<()> { - let depot = try!(Depot::new(String::from(config.path()))); - let mut views: Vec = vec![]; - let txn = try!(depot.datastore.views.txn_ro()); - let mut cursor = try!(txn.cursor_ro()); - match cursor.first() { - Err(BldrError {err: ErrorKind::MdbError(data_store::MdbError::NotFound), ..}) => { - outputln!("No repositories. Create one with `bldr repo-create`."); - return Ok(()); - } - Err(e) => return Err(e), - Ok((_, value)) => views.push(value), - } - loop { - match cursor.next() { - Ok((_, value)) => views.push(value), - Err(_) => break, - } - } - outputln!("Listing {} repositories", views.len()); - for view in views.iter() { - outputln!(" {}", view); - } - Ok(()) -} - -/// Starts the depot server. -/// -/// # Failures -/// -/// * Fails if the depot server fails to start - canot bind to the port, etc. -pub fn start(config: &Config) -> BldrResult<()> { - outputln!("Depot listening on {:?}", config.depot_addr()); - depot::run(&config) -} - -/// Analyzes the integrity of the depot's metadata by comparing the metadata with the packages -/// on disk. If a package is found on disk that is not present in the metadata it is added to the -/// metadata and if an entry in the metadata doesn't have a matching package archive on disk the -/// entry is dropped from the database. -/// -/// # Failures -/// -/// * The database cannot be read -/// * A write transaction cannot be acquired -pub fn repair(config: &Config) -> BldrResult<()> { - let depot = try!(Depot::new(String::from(config.path()))); - let report = try!(doctor::repair(&depot)); - outputln!("Report: {:?}", &report); - Ok(()) -} diff --git a/components/bldr/src/command/install.rs b/components/bldr/src/command/install.rs index 9716a654356..78694547b6e 100644 --- a/components/bldr/src/command/install.rs +++ b/components/bldr/src/command/install.rs @@ -47,10 +47,12 @@ use std::fs; -use fs::PACKAGE_CACHE; +use core::fs::PACKAGE_CACHE; +use core::package::PackageIdent; +use depot_core::data_object; +use depot_client; + use error::BldrResult; -use package::PackageIdent; -use depot::{self, data_object}; static LOGKEY: &'static str = "CI"; @@ -62,7 +64,7 @@ static LOGKEY: &'static str = "CI"; /// * Fails if it cannot create `/opt/bldr/cache/pkgs` /// * Fails if it cannot download the package from the upstream pub fn from_url>(url: &str, ident: &P) -> BldrResult { - let package = try!(depot::client::show_package(url, ident.as_ref())); + let package = try!(depot_client::show_package(url, ident.as_ref())); try!(fs::create_dir_all(PACKAGE_CACHE)); for dep in &package.tdeps { try!(install(url, &dep)); @@ -72,7 +74,7 @@ pub fn from_url>(url: &str, ident: &P) -> BldrResult>(url: &str, package: &P) -> BldrResult<()> { - let mut archive = try!(depot::client::fetch_package(url, package.as_ref(), PACKAGE_CACHE)); + let mut archive = try!(depot_client::fetch_package(url, package.as_ref(), PACKAGE_CACHE)); let package = try!(archive.ident()); try!(archive.unpack()); outputln!("Installed {}", package); diff --git a/components/bldr/src/command/key.rs b/components/bldr/src/command/key.rs index 02f8d11383d..a921b8f3633 100644 --- a/components/bldr/src/command/key.rs +++ b/components/bldr/src/command/key.rs @@ -9,16 +9,17 @@ use std::path::Path; use std::process::{Command, Stdio, Child}; use ansi_term::Colour::{Yellow, Red}; +use core::package::PackageIdent; +use core::fs::KEY_CACHE; +use core::gpg; +use depot_client; use time::strptime; use rpassword::read_password; use regex::Regex; use config::Config; use error::{BldrResult, ErrorKind}; -use fs::KEY_CACHE; -use package::{Package, PackageIdent}; -use depot; -use util::gpg; +use package::Package; static LOGKEY: &'static str = "KU"; // "key utils" static USER_KEY_COMMENT: &'static str = "bldr user key"; @@ -58,7 +59,7 @@ pub fn upload(config: &Config) -> BldrResult<()> { match fs::metadata(path) { Ok(_) => { outputln!("Uploading {}", config.key()); - try!(depot::client::put_key(url, path)); + try!(depot_client::put_key(url, path)); } Err(_) => { if path.components().count() == 1 { @@ -67,7 +68,7 @@ pub fn upload(config: &Config) -> BldrResult<()> { match fs::metadata(&cached) { Ok(_) => { outputln!("Uploading {}.asc", config.key()); - try!(depot::client::put_key(url, cached)); + try!(depot_client::put_key(url, cached)); } Err(_) => { return Err(bldr_error!(ErrorKind::KeyNotFound(config.key().to_string()))); @@ -116,7 +117,7 @@ pub fn import(config: &Config) -> BldrResult<()> { try!(fs::create_dir_all(KEY_CACHE)); // docopt requires -u to be set, so we should be safe to unwrap here let url = config.url().as_ref().unwrap(); - let filename = try!(depot::client::fetch_key(&url, &config.key(), KEY_CACHE)); + let filename = try!(depot_client::fetch_key(&url, &config.key(), KEY_CACHE)); try!(gpg::import(&filename)); } } diff --git a/components/bldr/src/command/mod.rs b/components/bldr/src/command/mod.rs index c113b300247..4cca1dfd8da 100644 --- a/components/bldr/src/command/mod.rs +++ b/components/bldr/src/command/mod.rs @@ -13,5 +13,4 @@ pub mod install; pub mod start; pub mod key; pub mod upload; -pub mod depot; pub mod configure; diff --git a/components/bldr/src/command/start.rs b/components/bldr/src/command/start.rs index 20b618dd10c..67237ac2845 100644 --- a/components/bldr/src/command/start.rs +++ b/components/bldr/src/command/start.rs @@ -48,17 +48,17 @@ //! See the [documentation on topologies](../topology) for a deeper discussion of how they function. //! -use ansi_term::Colour::Yellow; - use std::env; -use fs::PACKAGE_CACHE; +use ansi_term::Colour::Yellow; +use core::fs::PACKAGE_CACHE; +use depot_client; + use error::{BldrResult, ErrorKind}; use config::Config; -use package::{Package, PackageIdent}; +use package::Package; use topology::{self, Topology}; use command::install; -use depot; static LOGKEY: &'static str = "CS"; @@ -82,12 +82,12 @@ pub fn package(config: &Config) -> BldrResult<()> { // // 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 latest_pkg: Package = try!(depot::client::show_package(&url, config.package())) + let latest_pkg: Package = try!(depot_client::show_package(&url, config.package())) .into(); if latest_pkg > package { outputln!("Downloading latest version from remote: {}", &latest_pkg); - let archive = try!(depot::client::fetch_package(&url, - &PackageIdent::from(latest_pkg), + let archive = try!(depot_client::fetch_package(&url, + &latest_pkg.into(), PACKAGE_CACHE)); try!(archive.verify()); try!(archive.unpack()); diff --git a/components/bldr/src/command/upload.rs b/components/bldr/src/command/upload.rs index bcc2be1e706..c180eaa7c15 100644 --- a/components/bldr/src/command/upload.rs +++ b/components/bldr/src/command/upload.rs @@ -22,12 +22,12 @@ use std::path::PathBuf; +use core::package::PackageArchive; +use depot_client; use hyper::status::StatusCode; use error::{BldrResult, BldrError, ErrorKind}; use config::Config; -use package::archive::PackageArchive; -use depot; static LOGKEY: &'static str = "CU"; @@ -43,21 +43,21 @@ pub fn package(config: &Config) -> BldrResult<()> { let url = config.url().as_ref().unwrap(); let mut pa = PackageArchive::new(PathBuf::from(config.archive())); outputln!("Uploading from {}", pa.path.to_string_lossy()); - match depot::client::put_package(url, &mut pa) { + match depot_client::put_package(url, &mut pa) { Ok(()) => (), - Err(BldrError{err: ErrorKind::HTTP(StatusCode::Conflict), ..}) => { + Err(depot_client::Error::HTTP(StatusCode::Conflict)) => { outputln!("Package already exists on remote; skipping."); } - Err(BldrError{err: ErrorKind::HTTP(StatusCode::UnprocessableEntity), ..}) => { + Err(depot_client::Error::HTTP(StatusCode::UnprocessableEntity)) => { return Err(bldr_error!(ErrorKind::PackageArchiveMalformed(format!("{}", pa.path.to_string_lossy())))); } - Err(e @ BldrError{err: ErrorKind::HTTP(_), ..}) => { + Err(e @ depot_client::Error::HTTP(_)) => { outputln!("Unexpected response from remote"); - return Err(e); + return Err(BldrError::from(e)); } Err(e) => { outputln!("The package might exist on the remote - we fast abort, so.. :)"); - return Err(e); + return Err(BldrError::from(e)); } } outputln!("Complete"); diff --git a/components/bldr/src/config.rs b/components/bldr/src/config.rs index 2bec6b44b7d..79844f9c60c 100644 --- a/components/bldr/src/config.rs +++ b/components/bldr/src/config.rs @@ -12,13 +12,12 @@ //! //! See the [Config](struct.Config.html) struct for the specific options available. -use std::net; use std::str::FromStr; +use core::package::PackageIdent; + use error::{BldrError, ErrorKind}; use gossip::server::GOSSIP_DEFAULT_PORT; -use package::PackageIdent; -use depot; use topology::Topology; static LOGKEY: &'static str = "CFG"; @@ -39,10 +38,6 @@ pub enum Command { Encrypt, Decrypt, Shell, - Depot, - RepoCreate, - RepoList, - DepotRepair, Upload, } @@ -53,8 +48,6 @@ impl FromStr for Command { "bash" => Ok(Command::Shell), "config" => Ok(Command::Config), "decrypt" => Ok(Command::Decrypt), - "depot" => Ok(Command::Depot), - "depot-repair" => Ok(Command::DepotRepair), "download-depot-key" => Ok(Command::DownloadDepotKey), "encrypt" => Ok(Command::Encrypt), "export-key" => Ok(Command::ExportKey), @@ -63,8 +56,6 @@ impl FromStr for Command { "import-key" => Ok(Command::ImportKey), "install" => Ok(Command::Install), "list-keys" => Ok(Command::ListKeys), - "repo-create" => Ok(Command::RepoCreate), - "repo-list" => Ok(Command::RepoList), "sh" => Ok(Command::Shell), "start" => Ok(Command::Start), "upload-depot-key" => Ok(Command::UploadDepotKey), @@ -96,8 +87,6 @@ pub struct Config { password: Option, email: Option, expire_days: Option, - listen_addr: depot::ListenAddr, - port: depot::ListenPort, gossip_listen: String, userkey: Option, servicekey: Option, @@ -277,15 +266,6 @@ impl Config { &self.topology } - pub fn set_port(&mut self, port: u16) -> &mut Config { - self.port = depot::ListenPort(port); - self - } - - pub fn depot_addr(&self) -> net::SocketAddrV4 { - net::SocketAddrV4::new(self.listen_addr.0.clone(), self.port.0.clone()) - } - pub fn gossip_listen(&self) -> &str { &self.gossip_listen } diff --git a/components/bldr/src/error.rs b/components/bldr/src/error.rs index c32cf21a84a..cf5a6690b2d 100644 --- a/components/bldr/src/error.rs +++ b/components/bldr/src/error.rs @@ -38,6 +38,8 @@ use std::ffi; use std::sync::mpsc; use std::str; +use core::{self, package}; +use depot_client; use gpgme; use libarchive; use uuid; @@ -49,8 +51,7 @@ use toml; use mustache; use regex; -use depot::data_store; -use package; +use package::HookType; use output::StructuredOutput; static LOGKEY: &'static str = "ER"; @@ -88,11 +89,13 @@ impl BldrError { #[derive(Debug)] /// All the kinds of errors we produce. pub enum ErrorKind { + BldrCore(core::Error), ArchiveReadFailed(String), ArchiveError(libarchive::error::ArchiveError), Io(io::Error), CommandNotImplemented, DbInvalidPath, + DepotClient(depot_client::Error), InstallFailed, WriteSyncFailed, HyperError(hyper::error::Error), @@ -103,14 +106,11 @@ pub enum ErrorKind { UnpackFailed, TomlParser(Vec), TomlEncode(toml::Error), - MdbError(data_store::MdbError), MustacheEncoderError(mustache::encoder::Error), MetaFileNotFound(package::MetaFile), MetaFileMalformed(package::MetaFile), MetaFileIO(io::Error), PackageArchiveMalformed(String), - PermissionFailed, - BadVersion, RegexParse(regex::Error), ParseIntError(num::ParseIntError), FileNameError, @@ -131,7 +131,7 @@ pub enum ErrorKind { UnknownTopology(String), NoConfiguration, HealthCheck(String), - HookFailed(package::HookType, i32, String), + HookFailed(HookType, i32, String), TryRecvError(mpsc::TryRecvError), BadWatch(String), NoXFilename, @@ -155,10 +155,12 @@ impl fmt::Display for BldrError { // verbose on, and print it. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let content = match self.err { + ErrorKind::BldrCore(ref err) => format!("{}", err), ErrorKind::ArchiveReadFailed(ref e) => format!("Failed to read package archive, {}", e), ErrorKind::ArchiveError(ref err) => format!("{}", err), ErrorKind::Io(ref err) => format!("{}", err), ErrorKind::CommandNotImplemented => format!("Command is not yet implemented!"), + ErrorKind::DepotClient(ref err) => format!("{}", err), ErrorKind::DbInvalidPath => format!("Invalid filepath to internal datastore"), ErrorKind::InstallFailed => format!("Could not install package!"), ErrorKind::HyperError(ref err) => format!("{}", err), @@ -172,7 +174,6 @@ impl fmt::Display for BldrError { format!("Failed to parse toml:\n{}", toml_parser_string(errs)) } ErrorKind::TomlEncode(ref e) => format!("Failed to encode toml: {}", e), - ErrorKind::MdbError(ref err) => format!("{}", err), ErrorKind::MustacheEncoderError(ref me) => { match *me { mustache::encoder::Error::IoError(ref e) => format!("{}", e), @@ -190,8 +191,6 @@ impl fmt::Display for BldrError { format!("Package archive was unreadable or contained unexpected contents: {:?}", e) } - ErrorKind::PermissionFailed => format!("Failed to set permissions"), - ErrorKind::BadVersion => format!("Failed to parse a version number"), ErrorKind::RegexParse(ref e) => format!("{}", e), ErrorKind::ParseIntError(ref e) => format!("{}", e), ErrorKind::FileNameError => format!("Failed to extract a filename"), @@ -269,11 +268,13 @@ impl fmt::Display for BldrError { impl Error for BldrError { fn description(&self) -> &str { match self.err { + ErrorKind::BldrCore(ref err) => err.description(), ErrorKind::ArchiveError(ref err) => err.description(), ErrorKind::ArchiveReadFailed(_) => "Failed to read contents of package archive", ErrorKind::Io(ref err) => err.description(), ErrorKind::CommandNotImplemented => "Command is not yet implemented!", ErrorKind::DbInvalidPath => "A bad filepath was provided for an internal datastore", + ErrorKind::DepotClient(ref err) => err.description(), ErrorKind::InstallFailed => "Could not install package!", ErrorKind::WriteSyncFailed => "Could not write to destination; bytes written was 0 on a non-0 buffer", ErrorKind::CannotParseFileName => "Cannot determine the filename from the given URI", @@ -284,14 +285,11 @@ impl Error for BldrError { ErrorKind::UnpackFailed => "Failed to unpack a package", ErrorKind::TomlParser(_) => "Failed to parse toml!", ErrorKind::TomlEncode(_) => "Failed to encode toml!", - ErrorKind::MdbError(_) => "Database error", ErrorKind::MustacheEncoderError(_) => "Failed to encode mustache template", ErrorKind::MetaFileNotFound(_) => "Failed to read an archive's metafile", ErrorKind::MetaFileMalformed(_) => "MetaFile didn't contain a valid UTF-8 string", ErrorKind::MetaFileIO(_) => "MetaFile could not be read or written to", ErrorKind::PackageArchiveMalformed(_) => "Package archive was unreadable or had unexpected contents", - ErrorKind::PermissionFailed => "Failed to set permissions", - ErrorKind::BadVersion => "Failed to parse a version number", ErrorKind::RegexParse(_) => "Failed to parse a regular expression", ErrorKind::ParseIntError(_) => "Failed to parse an integer from a string!", ErrorKind::FileNameError => "Failed to extract a filename from a path", @@ -339,6 +337,18 @@ fn toml_parser_string(errs: &Vec) -> String { return errors; } +impl From for BldrError { + fn from(err: core::Error) -> BldrError { + bldr_error!(ErrorKind::BldrCore(err)) + } +} + +impl From for BldrError { + fn from(err: depot_client::Error) -> BldrError { + bldr_error!(ErrorKind::DepotClient(err)) + } +} + impl From for BldrError { fn from(err: uuid::ParseError) -> BldrError { bldr_error!(ErrorKind::UuidParseError(err)) @@ -411,12 +421,6 @@ impl From for BldrError { } } -impl From for BldrError { - fn from(err: data_store::MdbError) -> BldrError { - bldr_error!(ErrorKind::MdbError(err)) - } -} - impl From for BldrError { fn from(err: actor::ActorError) -> Self { bldr_error!(ErrorKind::ActorError(err)) diff --git a/components/bldr/src/lib.rs b/components/bldr/src/lib.rs index a48f4892695..c0f707bf08f 100644 --- a/components/bldr/src/lib.rs +++ b/components/bldr/src/lib.rs @@ -32,6 +32,9 @@ //! * [The bldr Depot; http based package repository](depot) //! +extern crate bldr_core as core; +extern crate bldr_depot_client as depot_client; +extern crate bldr_depot_core as depot_core; extern crate bincode; #[macro_use] extern crate bitflags; @@ -266,10 +269,8 @@ pub mod discovery; pub mod topology; pub mod state_machine; pub mod sidecar; -pub mod fs; pub mod health_check; pub mod config; -pub mod depot; pub mod user_config; pub mod service_config; pub mod census; diff --git a/components/bldr/src/main.rs b/components/bldr/src/main.rs index acdc076ac4a..8ca72aaeba3 100644 --- a/components/bldr/src/main.rs +++ b/components/bldr/src/main.rs @@ -6,6 +6,7 @@ #[macro_use] extern crate bldr; +extern crate bldr_core as core; extern crate rustc_serialize; #[macro_use] extern crate log; @@ -14,17 +15,19 @@ extern crate ansi_term; extern crate libc; #[macro_use] extern crate clap; -use clap::{App, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand}; -use std::str::FromStr; -use std::process; -use ansi_term::Colour::Yellow; + use std::ffi::CString; +use std::process; use std::ptr; +use std::str::FromStr; + +use ansi_term::Colour::Yellow; +use core::package::PackageIdent; +use clap::{App, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand}; use bldr::config::{Command, Config}; use bldr::error::{BldrResult, BldrError, ErrorKind}; use bldr::command::*; -use bldr::package::PackageIdent; use bldr::topology::Topology; /// Our output key @@ -91,10 +94,6 @@ fn config_from_args(args: &ArgMatches, t => return Err(bldr_error!(ErrorKind::UnknownTopology(String::from(t)))), } } - if sub_args.value_of("port").is_some() { - let p = value_t!(sub_args.value_of("port"), u16).unwrap_or_else(|e| e.exit()); - config.set_port(p); - } if sub_args.value_of("expire-days").is_some() { let ed = value_t!(sub_args.value_of("expire-days"), u16).unwrap_or_else(|e| e.exit()); config.set_expire_days(ed); @@ -206,42 +205,6 @@ fn main() { .help("If this service is a permanent gossip peer")); let sub_sh = SubCommand::with_name("sh").about("Start an interactive shell"); let sub_bash = SubCommand::with_name("bash").about("Start an interactive shell"); - let sub_depot = SubCommand::with_name("depot") - .about("Run a bldr package depot") - .arg(Arg::with_name("path") - .short("p") - .long("path") - .value_name("path") - .help("A path")) - .arg(Arg::with_name("port") - .long("port") - .value_name("port") - .help("Depot port. [default: 9632]")); - let sub_repair = SubCommand::with_name("depot-repair") - .about("Repair a bldr package depot") - .arg(Arg::with_name("path") - .short("p") - .long("path") - .value_name("path") - .help("A path")); - let sub_repo_list = SubCommand::with_name("repo-list") - .about("List repositories in the bldr depot") - .arg(Arg::with_name("path") - .short("p") - .long("path") - .value_name("path") - .help("A path")); - let sub_repo_create = SubCommand::with_name("repo-create") - .about("Create a new repository in the bldr depot") - .arg(Arg::with_name("repo") - .index(1) - .required(true) - .help("Name of the repository to create")) - .arg(Arg::with_name("path") - .short("p") - .long("path") - .value_name("path") - .help("A path")); let sub_upload = SubCommand::with_name("upload") .about("Upload an archive to a bldr depot") .arg(Arg::with_name("archive") @@ -370,10 +333,6 @@ fn main() { .subcommand(sub_start) .subcommand(sub_sh) .subcommand(sub_bash) - .subcommand(sub_depot) - .subcommand(sub_repair) - .subcommand(sub_repo_list) - .subcommand(sub_repo_create) .subcommand(sub_upload) .subcommand(sub_generate_user_key) .subcommand(sub_generate_service_key) @@ -401,10 +360,6 @@ fn main() { Command::Shell => shell(&config), Command::Config => configure(&config), Command::Decrypt => decrypt(&config), - Command::Depot => depot(&config), - Command::DepotRepair => repair(&config), - Command::RepoList => repo_list(&config), - Command::RepoCreate => repo_create(subcommand_matches.value_of("repo").unwrap(), &config), Command::DownloadDepotKey => download_depot_key(&config), Command::Encrypt => encrypt(&config), Command::ExportKey => export_key(&config), @@ -472,34 +427,6 @@ fn start(config: &Config) -> BldrResult<()> { Ok(()) } -/// Run a package Depot -#[allow(dead_code)] -fn depot(config: &Config) -> BldrResult<()> { - outputln!("Starting Bldr Depot at {}", - Yellow.bold().paint(config.path())); - try!(depot::start(&config)); - outputln!("Finished with {}", - Yellow.bold().paint(config.package().to_string())); - Ok(()) -} - -fn repair(config: &Config) -> BldrResult<()> { - outputln!("Verifying data integrity of Depot at {}", - Yellow.bold().paint(config.path())); - try!(depot::repair(config)); - Ok(()) -} - -fn repo_create(name: &str, config: &Config) -> BldrResult<()> { - try!(depot::create_repository(name, config)); - Ok(()) -} - -fn repo_list(config: &Config) -> BldrResult<()> { - try!(depot::list_repositories(config)); - Ok(()) -} - /// Upload a package #[allow(dead_code)] fn upload(config: &Config) -> BldrResult<()> { diff --git a/components/bldr/src/package/archive.rs b/components/bldr/src/package/archive.rs deleted file mode 100644 index 9bdbe18c1f7..00000000000 --- a/components/bldr/src/package/archive.rs +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. -// -// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing -// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software -// is made available under an open source license such as the Apache 2.0 License. - -use std::collections::HashMap; -use std::fmt; -use std::fs::File; -use std::io::{Read, Seek, SeekFrom}; -use std::path::PathBuf; -use std::str::{self, FromStr}; - -use crypto::sha2::Sha256; -use crypto::digest::Digest; -use libarchive::writer; -use libarchive::reader::{self, Reader}; -use libarchive::archive::{Entry, ReadFilter, ReadFormat}; -use regex::Regex; - -use error::{BldrResult, BldrError, ErrorKind}; -use package::PackageIdent; -use util::gpg; - -static LOGKEY: &'static str = "PA"; - -lazy_static! { - static ref METAFILE_REGXS: HashMap = { - let mut map = HashMap::new(); - map.insert(MetaFile::CFlags, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap()); - map.insert(MetaFile::Config, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap()); - map.insert(MetaFile::Deps, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap()); - map.insert(MetaFile::TDeps, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap()); - map.insert(MetaFile::Exposes, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap()); - map.insert(MetaFile::Ident, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap()); - map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap()); - map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap()); - map.insert(MetaFile::Manifest, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap()); - map.insert(MetaFile::Path, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap()); - map - }; -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub enum MetaFile { - CFlags, - Config, - Deps, - TDeps, - Exposes, - Ident, - LdRunPath, - LdFlags, - Manifest, - Path, -} - -impl fmt::Display for MetaFile { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let id = match *self { - MetaFile::CFlags => "CFLAGS", - MetaFile::Config => "default.toml", - MetaFile::Deps => "DEPS", - MetaFile::TDeps => "TDEPS", - MetaFile::Exposes => "EXPOSES", - MetaFile::Ident => "IDENT", - MetaFile::LdRunPath => "LD_RUN_PATH", - MetaFile::LdFlags => "LDFLAGS", - MetaFile::Manifest => "MANIFEST", - MetaFile::Path => "PATH", - }; - write!(f, "{}", id) - } -} - -type Metadata = HashMap; - -#[derive(Debug)] -pub struct PackageArchive { - pub path: PathBuf, - metadata: Option, -} - -impl PackageArchive { - pub fn new(path: PathBuf) -> Self { - PackageArchive { - path: path, - metadata: None, - } - } - - /// Calculate and return the checksum of the package archive in hexadecimal format. - /// - /// # Failures - /// - /// * If the archive cannot be read - pub fn checksum(&self) -> BldrResult { - let mut digest = Sha256::new(); - let mut file = try!(File::open(&self.path)); - let mut buffer = Vec::new(); - try!(file.read_to_end(&mut buffer)); - digest.input(&buffer); - let hash = digest.result_str(); - Ok(hash) - } - - pub fn cflags(&mut self) -> BldrResult> { - match self.read_metadata(MetaFile::CFlags) { - Ok(data) => Ok(data.cloned()), - Err(e) => Err(e), - } - } - - pub fn config(&mut self) -> BldrResult> { - match self.read_metadata(MetaFile::Config) { - Ok(data) => Ok(data.cloned()), - Err(e) => Err(e), - } - } - - /// Returns a list of package identifiers representing the runtime package dependencies for - /// this archive. - /// - /// # Failures - /// - /// * If a `DEPS` metafile is not found in the archive - /// * If the archive cannot be read - /// * If the archive cannot be verified - pub fn deps(&mut self) -> BldrResult> { - self.read_deps(MetaFile::Deps) - } - - /// Returns a list of package identifiers representing the transitive runtime package - /// dependencies for this archive. - /// - /// # Failures - /// - /// * If a `TDEPS` metafile is not found in the archive - /// * If the archive cannot be read - /// * If the archive cannot be verified - pub fn tdeps(&mut self) -> BldrResult> { - self.read_deps(MetaFile::TDeps) - } - - pub fn exposes(&mut self) -> BldrResult> { - match self.read_metadata(MetaFile::Exposes) { - Ok(Some(data)) => { - let ports: Vec = data.split(" ") - .filter_map(|port| port.parse::().ok()) - .collect(); - Ok(ports) - } - Ok(None) => Ok(vec![]), - Err(e) => Err(e), - } - } - - pub fn ident(&mut self) -> BldrResult { - match self.read_metadata(MetaFile::Ident) { - Ok(None) => Err(bldr_error!(ErrorKind::MetaFileMalformed(MetaFile::Ident))), - Ok(Some(data)) => PackageIdent::from_str(&data), - Err(e) => Err(e), - } - } - - pub fn ld_run_path(&mut self) -> BldrResult> { - match self.read_metadata(MetaFile::LdRunPath) { - Ok(data) => Ok(data.cloned()), - Err(e) => Err(e), - } - } - - pub fn ldflags(&mut self) -> BldrResult> { - match self.read_metadata(MetaFile::LdFlags) { - Ok(data) => Ok(data.cloned()), - Err(e) => Err(e), - } - } - - pub fn manifest(&mut self) -> BldrResult { - match self.read_metadata(MetaFile::Manifest) { - Ok(None) => Err(bldr_error!(ErrorKind::MetaFileMalformed(MetaFile::Manifest))), - Ok(Some(data)) => Ok(data.clone()), - Err(e) => Err(e), - } - } - - pub fn path(&mut self) -> BldrResult> { - match self.read_metadata(MetaFile::Path) { - Ok(data) => Ok(data.cloned()), - Err(e) => Err(e), - } - } - - /// A plain string representation of the archive's file name. - pub fn file_name(&self) -> String { - self.path.file_name().unwrap().to_string_lossy().into_owned() - } - - /// Given a package name and a path to a file as an `&str`, verify - /// the files gpg signature. - /// - /// # Failures - /// - /// * Fails if it cannot verify the GPG signature for any reason - pub fn verify(&self) -> BldrResult<()> { - match gpg::verify(self.path.to_str().unwrap()) { - Ok(_) => Ok(()), - Err(e) => Err(e), - } - } - - /// Given a package name and a path to a file as an `&str`, unpack - /// the package. - /// - /// # Failures - /// - /// * If the package cannot be unpacked via gpg - pub fn unpack(&self) -> BldrResult<()> { - let file = self.path.to_str().unwrap().to_string(); - let mut out = try!(gpg::verify(&file)); - try!(out.seek(SeekFrom::Start(0))); - let mut builder = reader::Builder::new(); - try!(builder.support_format(ReadFormat::All)); - try!(builder.support_filter(ReadFilter::All)); - let mut reader = try!(builder.open_stream(out)); - let writer = writer::Disk::new(); - try!(writer.set_standard_lookup()); - try!(writer.write(&mut reader, Some("/"))); - try!(writer.close()); - Ok(()) - } - - fn read_deps(&mut self, file: MetaFile) -> BldrResult> { - let mut deps: Vec = vec![]; - match self.read_metadata(file) { - Ok(Some(body)) => { - let ids: Vec = body.split("\n").map(|d| d.to_string()).collect(); - for id in &ids { - let package = try!(PackageIdent::from_str(id)); - if !package.fully_qualified() { - // JW TODO: use a more appropriate erorr to describe the invalid - // user input here. (user because a package was generated by a user - // and read into program) - return Err(bldr_error!(ErrorKind::InvalidPackageIdent(package.to_string()))); - } - deps.push(package); - } - Ok(deps) - } - Ok(None) => Ok(vec![]), - Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(deps), - Err(e) => Err(e), - } - } - - fn read_metadata(&mut self, file: MetaFile) -> BldrResult> { - if let Some(ref files) = self.metadata { - return Ok(files.get(&file)); - } - - let mut metadata = Metadata::new(); - let mut matched_count = 0u8; - let f = self.path.to_str().unwrap().to_string(); - let mut out = try!(gpg::verify(&f)); - try!(out.seek(SeekFrom::Start(0))); - let mut builder = reader::Builder::new(); - try!(builder.support_format(ReadFormat::All)); - try!(builder.support_filter(ReadFilter::All)); - let mut reader = try!(builder.open_stream(out)); - loop { - let mut matched_type: Option = None; - if let Some(entry) = reader.next_header() { - for (matched, regx) in METAFILE_REGXS.iter() { - if regx.is_match(entry.pathname()) { - matched_type = Some((*matched).clone()); - matched_count += 1; - break; - } - } - } else { - break; - } - - if matched_type.is_none() { - continue; - } - - match reader.read_block() { - Ok(Some(bytes)) => { - match str::from_utf8(bytes) { - Ok(content) => { - metadata.insert(matched_type.unwrap(), content.trim().to_string()); - } - Err(_) => { - return Err(bldr_error!(ErrorKind::MetaFileMalformed(matched_type.unwrap()))) - } - } - } - Ok(None) => (), - Err(_) => { - return Err(bldr_error!(ErrorKind::MetaFileMalformed(matched_type.unwrap()))) - } - } - - if matched_count == METAFILE_REGXS.len() as u8 { - break; - } - } - self.metadata = Some(metadata); - Ok(self.metadata.as_ref().unwrap().get(&file)) - } -} diff --git a/components/bldr/src/package/mod.rs b/components/bldr/src/package/mod.rs index 3c2d72de5eb..d5aced57e0a 100644 --- a/components/bldr/src/package/mod.rs +++ b/components/bldr/src/package/mod.rs @@ -4,11 +4,9 @@ // this file ("Licensee") apply to Licensee's use of the Software until such time that the Software // is made available under an open source license such as the Apache 2.0 License. -pub mod archive; pub mod hooks; pub mod updater; -pub use self::archive::{PackageArchive, MetaFile}; pub use self::updater::{PackageUpdater, PackageUpdaterActor, UpdaterMessage}; pub use self::hooks::HookType; @@ -22,14 +20,16 @@ use std::string::ToString; use std::io::prelude::*; use std::process::Command; use std::env; -use regex::Regex; + +use core::fs::{PACKAGE_CACHE, PACKAGE_HOME, SERVICE_HOME}; +use core::package::{version_sort, MetaFile, PackageIdent}; +use core::util; +use depot_core::data_object; use self::hooks::HookTable; -use fs::{PACKAGE_CACHE, PACKAGE_HOME, SERVICE_HOME}; use error::{BldrResult, BldrError, ErrorKind}; use health_check::{self, CheckResult}; use service_config::ServiceConfig; -use util; static LOGKEY: &'static str = "PK"; const INIT_FILENAME: &'static str = "init"; @@ -37,181 +37,6 @@ const HEALTHCHECK_FILENAME: &'static str = "health_check"; const RECONFIGURE_FILENAME: &'static str = "reconfigure"; const RUN_FILENAME: &'static str = "run"; -#[derive(Debug, Clone, RustcDecodable, RustcEncodable)] -pub struct Package { - pub origin: String, - pub name: String, - pub version: String, - pub release: String, - pub deps: Vec, - pub tdeps: Vec, -} - -impl fmt::Display for Package { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.ident()) - } -} - -#[derive(RustcEncodable, RustcDecodable, Eq, PartialEq, Debug, Clone)] -pub struct PackageIdent { - pub origin: String, - pub name: String, - pub version: Option, - pub release: Option, -} - -impl PackageIdent { - /// Creates a new package identifier - pub fn new>(origin: T, - name: T, - version: Option, - release: Option) - -> Self { - PackageIdent { - origin: origin.into(), - name: name.into(), - version: version.map(|v| v.into()), - release: release.map(|v| v.into()), - } - } - - pub fn fully_qualified(&self) -> bool { - self.version.is_some() && self.release.is_some() - } - - pub fn satisfies>(&self, ident: T) -> bool { - let other = ident.as_ref(); - if self.origin != other.origin || self.name != other.name { - return false; - } - if self.version.is_some() { - if other.version.is_none() { - return true; - } - if *self.version.as_ref().unwrap() != *other.version.as_ref().unwrap() { - return false; - } - } - if self.release.is_some() { - if other.release.is_none() { - return true; - } - if *self.release.as_ref().unwrap() != *other.release.as_ref().unwrap() { - return false; - } - } - true - } -} - -impl Default for PackageIdent { - fn default() -> PackageIdent { - PackageIdent::new("", "", None, None) - } -} - -impl fmt::Display for PackageIdent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.version.is_some() && self.release.is_some() { - write!(f, - "{}/{}/{}/{}", - self.origin, - self.name, - self.version.as_ref().unwrap(), - self.release.as_ref().unwrap()) - } else if self.version.is_some() { - write!(f, - "{}/{}/{}", - self.origin, - self.name, - self.version.as_ref().unwrap()) - } else { - write!(f, "{}/{}", self.origin, self.name) - } - } -} - -impl AsRef for PackageIdent { - fn as_ref(&self) -> &PackageIdent { - self - } -} - -impl From for PackageIdent { - fn from(value: Package) -> PackageIdent { - PackageIdent::new(value.origin, - value.name, - Some(value.version), - Some(value.release)) - } -} - -impl FromStr for PackageIdent { - type Err = BldrError; - - fn from_str(value: &str) -> Result { - let items: Vec<&str> = value.split("/").collect(); - let (origin, name, ver, rel) = match items.len() { - 2 => (items[0], items[1], None, None), - 3 => (items[0], items[1], Some(items[2]), None), - 4 => (items[0], items[1], Some(items[2]), Some(items[3])), - _ => return Err(bldr_error!(ErrorKind::InvalidPackageIdent(value.to_string()))), - }; - Ok(PackageIdent::new(origin, name, ver, rel)) - } -} - -impl PartialOrd for PackageIdent { - /// Packages can be compared according to the following: - /// - /// * origin is ignored in the comparison - my redis and - /// your redis compare the same. - /// * If the names are not equal, they cannot be compared. - /// * If the versions are greater/lesser, return that as - /// the ordering. - /// * If the versions are equal, return the greater/lesser - /// for the release. - fn partial_cmp(&self, other: &PackageIdent) -> Option { - if self.name != other.name { - return None; - } - if self.version.is_none() && other.version.is_none() { - return None; - } - if self.version.is_none() && other.version.is_some() { - return Some(Ordering::Less); - } - if self.version.is_some() && other.version.is_none() { - return Some(Ordering::Greater); - } - if self.release.is_none() && other.release.is_none() { - return None; - } - if self.release.is_none() && other.release.is_some() { - return Some(Ordering::Less); - } - if self.release.is_some() && other.release.is_none() { - return Some(Ordering::Greater); - } - let ord = match version_sort(self.version.as_ref().unwrap(), - other.version.as_ref().unwrap()) { - Ok(ord) => ord, - Err(e) => { - error!("This was a very bad version number: {:?}", e); - return None; - } - }; - match ord { - Ordering::Greater => return Some(Ordering::Greater), - Ordering::Less => return Some(Ordering::Less), - Ordering::Equal => { - return Some(self.release.cmp(&other.release)); - } - } - } -} - pub enum Signal { Status, Up, @@ -240,6 +65,16 @@ pub enum Signal { TryRestart, } +#[derive(Debug, Clone, RustcDecodable, RustcEncodable)] +pub struct Package { + pub origin: String, + pub name: String, + pub version: String, + pub release: String, + pub deps: Vec, + pub tdeps: Vec, +} + impl Package { pub fn deps>(ident: &PackageIdent, home: P) -> BldrResult> { Self::read_deps(home.as_ref().join(ident.to_string()), MetaFile::Deps) @@ -797,101 +632,30 @@ impl Package { } } -/// Sorts two packages according to their version. -/// -/// We are a bit more strict than your average package management solution on versioning. -/// What we support is the "some number of digits or dots" (the version number), -/// followed by an optional "-" and any alphanumeric string (the extension). When determining sort order, we: -/// -/// * Separate the version numbers from the extensions -/// * Split the version numbers into an array of digits on any '.' characters. Digits are convered -/// into . -/// * Compare the version numbers by iterating over them. If 'a' is greater or lesser than 'b', we -/// return that as the result. If it is equal, we move to the next digit and repeat. If one of -/// the version numbers is exhausted before the other, it gains 0's for the missing slot. -/// * If the version numbers are equal, but either A or B has an extension (but not both) than the -/// version without the extension is greater. (1.0.0 is greater than 1.0.0-alpha6) -/// * If both have an extension, it is compared lexicographically, with the result as the final -/// ordering. -/// -/// Returns a BldrError if we fail to match for any reason. -pub fn version_sort(a_version: &str, b_version: &str) -> BldrResult { - let (a_parts, a_extension) = try!(split_version(a_version)); - let (b_parts, b_extension) = try!(split_version(b_version)); - let mut a_iter = a_parts.iter(); - let mut b_iter = b_parts.iter(); - loop { - let mut a_exhausted = false; - let mut b_exhausted = false; - let a_num = match a_iter.next() { - Some(i) => try!(i.parse::()), - None => { - a_exhausted = true; - 0u64 - } - }; - let b_num = match b_iter.next() { - Some(i) => try!(i.parse::()), - None => { - b_exhausted = true; - 0u64 - } - }; - if a_exhausted && b_exhausted { - break; - } - match a_num.cmp(&b_num) { - Ordering::Greater => { - return Ok(Ordering::Greater); - } - Ordering::Equal => { - continue; - } - Ordering::Less => { - return Ok(Ordering::Less); - } - } +impl Into for Package { + fn into(self) -> PackageIdent { + PackageIdent::new(self.origin, self.name, Some(self.version), Some(self.release)) } +} - // If you have equal digits, and one has an extension, it is - // the plain digits who win. - // 1.0.0-alpha1 vs 1.0.0 - if a_extension.is_some() && b_extension.is_none() { - return Ok(Ordering::Less); - } else if a_extension.is_none() && b_extension.is_some() { - return Ok(Ordering::Greater); - } else if a_extension.is_none() && b_extension.is_none() { - return Ok(Ordering::Equal); - } else { - let a = match a_extension { - Some(a) => a, - None => String::new(), - }; - let b = match b_extension { - Some(b) => b, - None => String::new(), - }; - return Ok(a.cmp(&b)); +impl From for Package { + fn from(val: data_object::Package) -> Package { + let ident: &PackageIdent = val.ident.as_ref(); + Package { + origin: ident.origin.clone(), + name: ident.name.clone(), + version: ident.version.as_ref().unwrap().clone(), + release: ident.release.as_ref().unwrap().clone(), + deps: val.deps.into_iter().map(|d| d.into()).collect(), + tdeps: val.tdeps.into_iter().map(|d| d.into()).collect(), + } } } -fn split_version(version: &str) -> BldrResult<(Vec<&str>, Option)> { - let re = try!(Regex::new(r"([\d\.]+)(-.+)?")); - let caps = match re.captures(version) { - Some(caps) => caps, - None => return Err(bldr_error!(ErrorKind::BadVersion)), - }; - let version_number = caps.at(1).unwrap(); - let extension = match caps.at(2) { - Some(e) => { - let mut estr: String = e.to_string(); - estr.remove(0); - Some(estr) - } - None => None, - }; - let version_parts: Vec<&str> = version_number.split('.').collect(); - Ok((version_parts, extension)) +impl fmt::Display for Package { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.ident()) + } } impl PartialEq for Package { @@ -940,152 +704,3 @@ impl PartialOrd for Package { } } } - -#[cfg(test)] -mod tests { - use super::{PackageIdent, split_version, version_sort}; - use std::cmp::Ordering; - use std::cmp::PartialOrd; - - #[test] - fn package_ident_partial_eq() { - let a = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - let b = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - assert_eq!(a, b); - } - - #[test] - fn package_ident_partial_ord() { - let a = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.1".to_string()), - Some("20150521131555".to_string())); - let b = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - match a.partial_cmp(&b) { - Some(ord) => assert_eq!(ord, Ordering::Greater), - None => panic!("Ordering should be greater"), - } - } - - #[test] - fn package_ident_partial_ord_bad_name() { - let a = PackageIdent::new("bldr".to_string(), - "snoopy".to_string(), - Some("1.0.1".to_string()), - Some("20150521131555".to_string())); - let b = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - match a.partial_cmp(&b) { - Some(_) => panic!("We tried to return an order"), - None => assert!(true), - } - } - - #[test] - fn package_ident_partial_ord_different_origin() { - let a = PackageIdent::new("adam".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - let b = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - match a.partial_cmp(&b) { - Some(ord) => assert_eq!(ord, Ordering::Equal), - None => panic!("We failed to return an order"), - } - } - - #[test] - fn package_ident_partial_ord_release() { - let a = PackageIdent::new("adam".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131556".to_string())); - let b = PackageIdent::new("bldr".to_string(), - "bldr".to_string(), - Some("1.0.0".to_string()), - Some("20150521131555".to_string())); - match a.partial_cmp(&b) { - Some(ord) => assert_eq!(ord, Ordering::Greater), - None => panic!("We failed to return an order"), - } - } - - #[test] - fn split_version_returns_both_parts() { - let svr = split_version("1.2.3-beta16"); - match svr { - Ok((version_parts, Some(extension))) => { - assert_eq!(vec!["1", "2", "3"], version_parts); - assert_eq!("beta16", extension); - } - Ok((_, None)) => panic!("Has an extension"), - Err(e) => panic!("{:?}", e), - } - } - - #[test] - fn version_sort_simple() { - match version_sort("1.0.0", "2.0.0") { - Ok(compare) => assert_eq!(compare, Ordering::Less), - Err(e) => panic!("{:?}", e), - } - match version_sort("2.0.1", "2.0.0") { - Ok(compare) => assert_eq!(compare, Ordering::Greater), - Err(e) => panic!("{:?}", e), - } - match version_sort("2.1.1", "2.1.1") { - Ok(compare) => assert_eq!(compare, Ordering::Equal), - Err(e) => panic!("{:?}", e), - } - match version_sort("20150521131347", "20150521131346") { - Ok(compare) => assert_eq!(compare, Ordering::Greater), - Err(e) => panic!("{:?}", e), - } - } - - #[test] - fn version_sort_complex() { - match version_sort("1.0.0-alpha2", "1.0.0-alpha1") { - Ok(compare) => assert_eq!(compare, Ordering::Greater), - Err(e) => panic!("{:?}", e), - } - match version_sort("1.0.0-alpha1", "1.0.0-alpha2") { - Ok(compare) => assert_eq!(compare, Ordering::Less), - Err(e) => panic!("{:?}", e), - } - match version_sort("1.0.0-beta1", "1.0.0-alpha1000") { - Ok(compare) => assert_eq!(compare, Ordering::Greater), - Err(e) => panic!("{:?}", e), - } - match version_sort("2.1.1", "2.1.1-alpha2") { - Ok(compare) => assert_eq!(compare, Ordering::Greater), - Err(e) => panic!("{:?}", e), - } - match version_sort("2.1.1-alpha2", "2.1.1") { - Ok(compare) => assert_eq!(compare, Ordering::Less), - Err(e) => panic!("{:?}", e), - } - } - - #[test] - fn check_fully_qualified_package_id() { - let partial = PackageIdent::new("chef", "libarchive", None, None); - let full = PackageIdent::new("chef", "libarchive", Some("1.2.3"), Some("1234")); - assert!(!partial.fully_qualified()); - assert!(full.fully_qualified()); - } -} diff --git a/components/bldr/src/package/updater.rs b/components/bldr/src/package/updater.rs index 11623611547..e11a22a23a5 100644 --- a/components/bldr/src/package/updater.rs +++ b/components/bldr/src/package/updater.rs @@ -7,13 +7,14 @@ use std::sync::{Arc, RwLock}; use std::str::FromStr; +use core::fs::PACKAGE_CACHE; +use core::package::PackageIdent; +use depot_client; use wonder; use wonder::actor::{GenServer, InitResult, HandleResult, ActorSender, ActorResult}; use error::BldrError; -use fs::PACKAGE_CACHE; -use package::{Package, PackageIdent}; -use depot; +use package::Package; const TIMEOUT_MS: u64 = 60_000; @@ -87,14 +88,14 @@ impl GenServer for PackageUpdater { // This will allow an operator to lock to a version and receive security updates // in the form of release updates for a package. let ident = PackageIdent::new(package.origin.clone(), package.name.clone(), None, None); - match depot::client::show_package(&state.depot, &ident) { + match depot_client::show_package(&state.depot, &ident) { Ok(remote) => { let latest: Package = remote.into(); if latest > *package { - match depot::client::fetch_package(&state.depot, - &PackageIdent::from_str(&latest.ident()) - .unwrap(), - PACKAGE_CACHE) { + match depot_client::fetch_package(&state.depot, + &PackageIdent::from_str(&latest.ident()) + .unwrap(), + PACKAGE_CACHE) { Ok(archive) => { debug!("Updater downloaded new package to {:?}", archive); // JW TODO: actually handle verify and unpack results diff --git a/components/bldr/src/topology/standalone.rs b/components/bldr/src/topology/standalone.rs index e8ea2c5aaff..b4d83977351 100644 --- a/components/bldr/src/topology/standalone.rs +++ b/components/bldr/src/topology/standalone.rs @@ -18,9 +18,11 @@ use std::thread; use std::process::{Command, Stdio}; use std::io::prelude::*; -use fs::SERVICE_HOME; +use core::package::PackageIdent; +use core::fs::SERVICE_HOME; + use error::{BldrResult, BldrError, ErrorKind}; -use package::{Package, PackageIdent}; +use package::Package; use state_machine::StateMachine; use topology::{self, State, Worker}; use config::Config; diff --git a/components/bldr/src/util/mod.rs b/components/bldr/src/util/mod.rs index 543be4ae9f1..b36dabe32b0 100644 --- a/components/bldr/src/util/mod.rs +++ b/components/bldr/src/util/mod.rs @@ -5,8 +5,6 @@ // is made available under an open source license such as the Apache 2.0 License. pub mod convert; -pub mod gpg; -pub mod perm; pub mod sys; pub mod signals; diff --git a/components/bldr/tests/bldr/depot.rs b/components/bldr/tests/bldr/depot.rs deleted file mode 100644 index 75633c8b726..00000000000 --- a/components/bldr/tests/bldr/depot.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. -// -// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing -// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software -// is made available under an open source license such as the Apache 2.0 License. - -use setup; -use util::{command, docker}; - -#[test] -#[ignore] -fn upload_a_package_and_then_install_it() { - setup::gpg_import(); - setup::key_install(); - setup::simple_service(); - let d = docker::depot("test/simple_service"); - let ipaddress = d.ipaddress(); - - let mut upload = command::bldr(&["upload", - "test/simple_service", - "-u", - &format!("http://{}:9632", ipaddress)]) - .unwrap(); - upload.wait_with_output(); - assert_cmd_exit_code!(upload, [0]); - assert_regex!(upload.stdout(), r"Upload Bldr Package (.+)"); - assert_regex!(upload.stdout(), r"Uploading from (.+)"); - assert_regex!(upload.stdout(), r"Complete"); - let mut install = command::bldr(&["install", - "test/simple_service", - "-u", - &format!("http://{}:9632", ipaddress)]) - .unwrap(); - install.wait_with_output(); - assert_cmd_exit_code!(install, [0]); -} diff --git a/components/bldr/tests/bldr/key.rs b/components/bldr/tests/bldr/key.rs index bdbc5d18a1f..d60f5fc6dcf 100644 --- a/components/bldr/tests/bldr/key.rs +++ b/components/bldr/tests/bldr/key.rs @@ -300,7 +300,8 @@ fn mk_tmp_filename() -> String { #[test] fn kt_find_key() { - use bldr_lib::util::gpg; + use core::gpg; + let cache_dir = gen_test_gpg_cache(); // let cache_dir = "/opt/bldr/cache/gpg/"; { diff --git a/components/bldr/tests/functional.rs b/components/bldr/tests/functional.rs index ee8feb68cd6..a9d5247cc36 100644 --- a/components/bldr/tests/functional.rs +++ b/components/bldr/tests/functional.rs @@ -10,6 +10,7 @@ extern crate time; extern crate hyper; extern crate url; extern crate bldr as bldr_lib; +extern crate bldr_core as core; extern crate uuid; extern crate rustc_serialize; diff --git a/components/core/Cargo.lock b/components/core/Cargo.lock new file mode 100644 index 00000000000..a4da8cd1f90 --- /dev/null +++ b/components/core/Cargo.lock @@ -0,0 +1,192 @@ +[root] +name = "bldr_core" +version = "0.4.0" +dependencies = [ + "gpgme 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libarchive 0.1.0", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gcc" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gpg-error" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpg-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpgme-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libarchive" +version = "0.1.0" +dependencies = [ + "libarchive3-sys 0.1.0", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libarchive3-sys" +version = "0.1.0" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libgpg-error-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rust-crypto" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "time" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/components/core/Cargo.toml b/components/core/Cargo.toml new file mode 100644 index 00000000000..a7c74c0d5a1 --- /dev/null +++ b/components/core/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bldr_core" +version = "0.4.0" +authors = ["Adam Jacob ", "Jamie Winsor ", "Fletcher Nichol ", "Joshua Timberman ", "Dave Parfitt "] + +[dependencies] +gpgme = "*" +lazy_static = "*" +log = "*" +regex = "*" +rust-crypto = "*" +rustc-serialize = "*" + +[dependencies.libarchive] +path = "../../vendor/libarchive-rust" diff --git a/components/core/src/error.rs b/components/core/src/error.rs new file mode 100644 index 00000000000..9261781487c --- /dev/null +++ b/components/core/src/error.rs @@ -0,0 +1,119 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::error; +use std::io; +use std::fmt; +use std::num; +use std::result; +use std::string; + +use gpgme; +use libarchive; +use regex; + +use package; + +pub type Result = result::Result; + +#[derive(Debug)] +pub enum Error { + ArchiveError(libarchive::error::ArchiveError), + FileNotFound(String), + GPG(gpgme::Error), + InvalidKeyParameter(String), + InvalidPackageIdent(String), + IO(io::Error), + MetaFileNotFound(package::MetaFile), + MetaFileMalformed(package::MetaFile), + ParseIntError(num::ParseIntError), + PermissionFailed, + RegexParse(regex::Error), + StringFromUtf8Error(string::FromUtf8Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match *self { + Error::ArchiveError(ref err) => format!("{}", err), + Error::FileNotFound(ref e) => format!("File not found at: {}", e), + Error::GPG(ref e) => format!("{}", e), + Error::InvalidKeyParameter(ref e) => { + format!("Invalid parameter for key generation: {:?}", e) + } + Error::InvalidPackageIdent(ref e) => { + format!("Invalid package identifier: {:?}. A valid identifier is in the form \ + origin/name (example: chef/redis)", + e) + } + Error::IO(ref err) => format!("{}", err), + Error::MetaFileNotFound(ref e) => format!("Couldn't read MetaFile: {}, not found", e), + Error::MetaFileMalformed(ref e) => { + format!("MetaFile: {:?}, didn't contain a valid UTF-8 string", e) + } + Error::ParseIntError(ref e) => format!("{}", e), + Error::PermissionFailed => format!("Failed to set permissions"), + Error::RegexParse(ref e) => format!("{}", e), + Error::StringFromUtf8Error(ref e) => format!("{}", e), + }; + write!(f, "{}", msg) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::ArchiveError(ref err) => err.description(), + Error::FileNotFound(_) => "File not found", + Error::GPG(_) => "gpgme error", + Error::InvalidKeyParameter(_) => "Key parameter error", + Error::InvalidPackageIdent(_) => "Package identifiers must be in origin/name format (example: chef/redis)", + Error::IO(ref err) => err.description(), + Error::MetaFileNotFound(_) => "Failed to read an archive's metafile", + Error::MetaFileMalformed(_) => "MetaFile didn't contain a valid UTF-8 string", + Error::ParseIntError(_) => "Failed to parse an integer from a string!", + Error::PermissionFailed => "Failed to set permissions", + Error::RegexParse(_) => "Failed to parse a regular expression", + Error::StringFromUtf8Error(_) => "Failed to convert a string from a Vec as UTF-8", + } + } +} + +impl From for Error { + fn from(err: string::FromUtf8Error) -> Error { + Error::StringFromUtf8Error(err) + } +} + +impl From for Error { + fn from(err: gpgme::Error) -> Error { + Error::GPG(err) + } +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IO(err) + } +} + +impl From for Error { + fn from(err: libarchive::error::ArchiveError) -> Error { + Error::ArchiveError(err) + } +} + +impl From for Error { + fn from(err: num::ParseIntError) -> Error { + Error::ParseIntError(err) + } +} + +impl From for Error { + fn from(err: regex::Error) -> Error { + Error::RegexParse(err) + } +} diff --git a/components/bldr/src/fs.rs b/components/core/src/fs.rs similarity index 100% rename from components/bldr/src/fs.rs rename to components/core/src/fs.rs diff --git a/components/bldr/src/util/gpg.rs b/components/core/src/gpg.rs similarity index 82% rename from components/bldr/src/util/gpg.rs rename to components/core/src/gpg.rs index 763d1fcdace..3812728066d 100644 --- a/components/bldr/src/util/gpg.rs +++ b/components/core/src/gpg.rs @@ -8,13 +8,15 @@ use std::fs::{self, File}; use std::io; use std::io::Write; use std::env; +use std::result; + use gpgme; use gpgme::ops; + +use error::{Error, Result}; use fs::GPG_CACHE; -use error::{BldrResult, BldrError, ErrorKind}; use util::perm; -static LOGKEY: &'static str = "KEY"; static BLDR_GPG_CACHE_ENV_VAR: &'static str = "BLDR_GPG_CACHE"; static DEFAULT_KEY_TYPE: &'static str = "RSA"; @@ -61,8 +63,8 @@ impl<'a> KeygenParams<'a> { None => "".to_string(), }; -// NOTE: you can't pass Passphrase: with an empty string -// so don't pass the string at all if a value doesn't exist + // NOTE: you can't pass Passphrase: with an empty string + // so don't pass the string at all if a value doesn't exist format!(" Key-Type: {key_type} @@ -100,7 +102,7 @@ fn gpg_cache_dir() -> String { /// Initialize the gpgme context w/ OpenPGP protocol and /// GPG cache directory (which may come from an env var) -fn init_ctx() -> BldrResult { +fn init_ctx() -> Result { let mut ctx = try!(gpgme::create_context()); try!(ctx.set_engine_info(gpgme::PROTOCOL_OPENPGP, None, Some(gpg_cache_dir()))); Ok(ctx) @@ -108,23 +110,23 @@ fn init_ctx() -> BldrResult { /// Create the GPG cache directory if it doesn't exist /// Set the permissions on the directory so the user has r/w/x only -fn ensure_gpg_dir() -> BldrResult<()> { +fn ensure_gpg_dir() -> Result<()> { try!(fs::create_dir_all(gpg_cache_dir())); try!(perm::set_permissions(&gpg_cache_dir(), CACHE_DIR_PERMS)); Ok(()) } -pub fn decrypt<'a>(file: &str) -> BldrResult> { +pub fn decrypt<'a>(file: &str) -> Result> { let mut ctx = try!(init_ctx()); try!(ensure_gpg_dir()); let mut signature = { let f = match File::open(&file) { Ok(f) => f, - Err(e) => return Err(BldrError::from(e)), + Err(e) => return Err(Error::from(e)), }; match gpgme::Data::from_seekable_reader(f) { Ok(data) => data, - Err(wrapped_error) => return Err(BldrError::from(wrapped_error.error())), + Err(wrapped_error) => return Err(Error::from(wrapped_error.error())), } }; let mut out = try!(gpgme::Data::new()); @@ -140,17 +142,17 @@ pub fn encrypt_and_sign(userkey: &str, servicekey: &str, infile: &str, outfile: &str) - -> BldrResult<()> { + -> Result<()> { let mut ctx = try!(init_ctx()); let mut infiledata = { let f = match File::open(&infile) { Ok(f) => f, - Err(e) => return Err(BldrError::from(e)), + Err(e) => return Err(Error::from(e)), }; match gpgme::Data::from_seekable_reader(f) { Ok(data) => data, - Err(wrapped_error) => return Err(BldrError::from(wrapped_error.error())), + Err(wrapped_error) => return Err(Error::from(wrapped_error.error())), } }; @@ -160,21 +162,15 @@ pub fn encrypt_and_sign(userkey: &str, // let ukey2 = try!(find_key(userkey)); let skey = try!(find_key(servicekey)); - if let None = ukey { - return Err(bldr_error!(ErrorKind::InvalidKeyParameter(String::from("User key not found")))); + if ukey.is_none() { + return Err(Error::InvalidKeyParameter(String::from("User key not found"))); } - // - // if let None = ukey2 { - // return Err(bldr_error!(ErrorKind::InvalidKeyParameter(String::from("User key not found")))); - // } - // - if let None = skey { - return Err(bldr_error!(ErrorKind::InvalidKeyParameter(String::from("Service key not \ - found")))); + if skey.is_none() { + return Err(Error::InvalidKeyParameter(String::from("Service key not \ + found"))); } let ukey = ukey.unwrap(); - // let ukey2 = ukey2.unwrap(); let skey = skey.unwrap(); let recipients = vec![skey, ukey]; @@ -219,16 +215,16 @@ pub fn encrypt_and_sign(userkey: &str, /// Decrypt uses a service private key and a user public key to verify an encrypted message. /// A service OR a user should be able to decrypt bldr-encrypted a message. -pub fn decrypt_and_verify(infile: &str, outfile: &str) -> BldrResult<()> { +pub fn decrypt_and_verify(infile: &str, outfile: &str) -> Result<()> { let mut ctx = try!(init_ctx()); let mut infiledata = { let f = match File::open(&infile) { Ok(f) => f, - Err(e) => return Err(BldrError::from(e)), + Err(e) => return Err(Error::from(e)), }; match gpgme::Data::from_seekable_reader(f) { Ok(data) => data, - Err(wrapped_error) => return Err(BldrError::from(wrapped_error.error())), + Err(wrapped_error) => return Err(Error::from(wrapped_error.error())), } }; let mut output = try!(gpgme::Data::new()); @@ -243,7 +239,7 @@ pub fn decrypt_and_verify(infile: &str, outfile: &str) -> BldrResult<()> { debug!("signatures? {:?}", verify_result.signatures()); debug!("filename? {:?}", verify_result.filename()); } - Err(e) => return Err(BldrError::from(e)), + Err(e) => return Err(Error::from(e)), }; try!(write_output(outfile, output)); @@ -292,7 +288,7 @@ fn debug_import_result(result: ops::ImportResult) { } /// Import a public key into the GPG cache -pub fn import(keyfile: &str) -> BldrResult<()> { +pub fn import(keyfile: &str) -> Result<()> { try!(ensure_gpg_dir()); let mut ctx = try!(init_ctx()); let mut data = try!(gpgme::Data::load(&keyfile)); @@ -302,16 +298,16 @@ pub fn import(keyfile: &str) -> BldrResult<()> { debug_import_result(result); Ok(()) } - Err(e) => Err(BldrError::from(e)), + Err(e) => Err(Error::from(e)), } } /// Export a public key from the GPG cache. -pub fn export(key: &str, outfile: &str) -> BldrResult<()> { +pub fn export(key: &str, outfile: &str) -> Result<()> { let mut ctx = try!(init_ctx()); let key_search = try!(find_key(&key)); - if let None = key_search { - return Err(bldr_error!(ErrorKind::InvalidKeyParameter(String::from("Key not found")))); + if key_search.is_none() { + return Err(Error::InvalidKeyParameter(String::from("Key not found"))); } let k = key_search.unwrap(); let keys = vec![k]; @@ -319,31 +315,31 @@ pub fn export(key: &str, outfile: &str) -> BldrResult<()> { let mut output = gpgme::Data::new().unwrap(); ctx.set_armor(true); if let Err(e) = ctx.export_keys(&keys, mode, Some(&mut output)) { - return Err(BldrError::from(e)); + return Err(Error::from(e)); }; try!(write_output(outfile, output)); Ok(()) } -pub fn verify<'a>(file: &str) -> BldrResult> { +pub fn verify<'a>(file: &str) -> Result> { let mut ctx = try!(init_ctx()); let mut signature = { let f = match File::open(&file) { Ok(f) => f, - Err(_) => return Err(bldr_error!(ErrorKind::FileNotFound(String::from(file)))), + Err(_) => return Err(Error::FileNotFound(String::from(file))), }; match gpgme::Data::from_seekable_reader(f) { Ok(data) => data, - Err(wrapped_error) => return Err(BldrError::from(wrapped_error.error())), + Err(e) => return Err(Error::from(e.error())), } }; let mut plain = try!(gpgme::Data::new()); match ctx.verify(&mut signature, None, Some(&mut plain)) { Ok(_) => Ok(plain), - Err(e) => Err(BldrError::from(e)), + Err(e) => Err(Error::from(e)), } } @@ -351,12 +347,12 @@ pub fn verify<'a>(file: &str) -> BldrResult> { /// in the GPG cache. /// `RUST_LOG=bldr=debug` is your friend for this one, as the `ctx.generate_key` /// function takes an ugly xml-ish string. -pub fn generate(params: &KeygenParams) -> BldrResult { +pub fn generate(params: &KeygenParams) -> Result { try!(ensure_gpg_dir()); let k = try!(find_key(¶ms.keyname)); if k.is_some() { - return Err(bldr_error!(ErrorKind::InvalidKeyParameter(String::from("Key already exists")))); + return Err(Error::InvalidKeyParameter(String::from("Key already exists"))); } let mut ctx = try!(init_ctx()); @@ -372,19 +368,19 @@ pub fn generate(params: &KeygenParams) -> BldrResult { None => Ok("No fingerprint".to_string()), } } - Err(e) => Err(BldrError::from(e)), + Err(e) => Err(Error::from(e)), } } /// Return the **first** key that contains the `keyname` **first** in it's list of users -pub fn find_key(keyname: &str) -> BldrResult> { +pub fn find_key(keyname: &str) -> Result> { try!(ensure_gpg_dir()); let mut ctx = try!(init_ctx()); let mode = ops::KeyListMode::empty(); ctx.set_key_list_mode(mode).unwrap(); let mut keys = try!(ctx.keys()); // irritatingly nested - for key in keys.by_ref().filter_map(Result::ok) { + for key in keys.by_ref().filter_map(result::Result::ok) { match key.user_ids().enumerate().next() { Some((_, user)) => { if let Some(n) = user.name() { @@ -401,7 +397,7 @@ pub fn find_key(keyname: &str) -> BldrResult> { /// query all keys in the gpg cache and return a vec /// with a copy of each -pub fn list() -> BldrResult> { +pub fn list() -> Result> { try!(ensure_gpg_dir()); let mut ctx = try!(init_ctx()); @@ -414,25 +410,21 @@ pub fn list() -> BldrResult> { let mut allkeys = Vec::new(); // get ALL keys let mut keys = ctx.keys().unwrap(); - for key in keys.by_ref().filter_map(Result::ok) { + for key in keys.by_ref().filter_map(result::Result::ok) { allkeys.push(key.clone()); } Ok(allkeys) } /// write the output from a gpgme::Data object to a file -fn write_output(outfile: &str, output: gpgme::Data) -> BldrResult<()> { +fn write_output(outfile: &str, output: gpgme::Data) -> Result<()> { match output.into_string() { Ok(o) => { let mut f = try!(File::create(outfile)); try!(f.write_all(o.as_bytes())); + Ok(()) } - Err(e) => { - match e { - Some(utf8_error) => return Err(BldrError::from(utf8_error)), - None => panic!("File output error: {:?}", e), - } - } + Err(Some(e)) => Err(Error::from(e)), + Err(None) => panic!("File output error"), } - Ok(()) } diff --git a/components/core/src/lib.rs b/components/core/src/lib.rs new file mode 100644 index 00000000000..11ee869def8 --- /dev/null +++ b/components/core/src/lib.rs @@ -0,0 +1,23 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +extern crate crypto; +extern crate gpgme; +#[macro_use] +extern crate lazy_static; +extern crate libarchive; +#[macro_use] +extern crate log; +extern crate regex; +extern crate rustc_serialize; + +pub use self::error::{Error, Result}; + +pub mod error; +pub mod fs; +pub mod gpg; +pub mod package; +pub mod util; diff --git a/components/core/src/package.rs b/components/core/src/package.rs new file mode 100644 index 00000000000..de57d5e760d --- /dev/null +++ b/components/core/src/package.rs @@ -0,0 +1,705 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::cmp::{Ordering, PartialOrd}; +use std::collections::HashMap; +use std::fmt; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom}; +use std::path::PathBuf; +use std::result; +use std::str::{self, FromStr}; + +use crypto::sha2::Sha256; +use crypto::digest::Digest; +use libarchive::writer; +use libarchive::reader::{self, Reader}; +use libarchive::archive::{Entry, ReadFilter, ReadFormat}; +use regex::Regex; + +use error::{Error, Result}; +use gpg; + +lazy_static! { + static ref METAFILE_REGXS: HashMap = { + let mut map = HashMap::new(); + map.insert(MetaFile::CFlags, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap()); + map.insert(MetaFile::Config, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap()); + map.insert(MetaFile::Deps, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap()); + map.insert(MetaFile::TDeps, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap()); + map.insert(MetaFile::Exposes, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap()); + map.insert(MetaFile::Ident, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap()); + map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap()); + map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap()); + map.insert(MetaFile::Manifest, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap()); + map.insert(MetaFile::Path, Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap()); + map + }; +} + +#[derive(RustcEncodable, RustcDecodable, Eq, PartialEq, Debug, Clone)] +pub struct PackageIdent { + pub origin: String, + pub name: String, + pub version: Option, + pub release: Option, +} + +impl PackageIdent { + /// Creates a new package identifier + pub fn new>(origin: T, + name: T, + version: Option, + release: Option) + -> Self { + PackageIdent { + origin: origin.into(), + name: name.into(), + version: version.map(|v| v.into()), + release: release.map(|v| v.into()), + } + } + + pub fn fully_qualified(&self) -> bool { + self.version.is_some() && self.release.is_some() + } + + pub fn satisfies>(&self, ident: T) -> bool { + let other = ident.as_ref(); + if self.origin != other.origin || self.name != other.name { + return false; + } + if self.version.is_some() { + if other.version.is_none() { + return true; + } + if *self.version.as_ref().unwrap() != *other.version.as_ref().unwrap() { + return false; + } + } + if self.release.is_some() { + if other.release.is_none() { + return true; + } + if *self.release.as_ref().unwrap() != *other.release.as_ref().unwrap() { + return false; + } + } + true + } +} + +impl Default for PackageIdent { + fn default() -> PackageIdent { + PackageIdent::new("", "", None, None) + } +} + +impl fmt::Display for PackageIdent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.version.is_some() && self.release.is_some() { + write!(f, + "{}/{}/{}/{}", + self.origin, + self.name, + self.version.as_ref().unwrap(), + self.release.as_ref().unwrap()) + } else if self.version.is_some() { + write!(f, + "{}/{}/{}", + self.origin, + self.name, + self.version.as_ref().unwrap()) + } else { + write!(f, "{}/{}", self.origin, self.name) + } + } +} + +impl AsRef for PackageIdent { + fn as_ref(&self) -> &PackageIdent { + self + } +} + +impl FromStr for PackageIdent { + type Err = Error; + + fn from_str(value: &str) -> result::Result { + let items: Vec<&str> = value.split("/").collect(); + let (origin, name, ver, rel) = match items.len() { + 2 => (items[0], items[1], None, None), + 3 => (items[0], items[1], Some(items[2]), None), + 4 => (items[0], items[1], Some(items[2]), Some(items[3])), + _ => return Err(Error::InvalidPackageIdent(value.to_string())), + }; + Ok(PackageIdent::new(origin, name, ver, rel)) + } +} + +impl PartialOrd for PackageIdent { + /// Packages can be compared according to the following: + /// + /// * origin is ignored in the comparison - my redis and + /// your redis compare the same. + /// * If the names are not equal, they cannot be compared. + /// * If the versions are greater/lesser, return that as + /// the ordering. + /// * If the versions are equal, return the greater/lesser + /// for the release. + fn partial_cmp(&self, other: &PackageIdent) -> Option { + if self.name != other.name { + return None; + } + if self.version.is_none() && other.version.is_none() { + return None; + } + if self.version.is_none() && other.version.is_some() { + return Some(Ordering::Less); + } + if self.version.is_some() && other.version.is_none() { + return Some(Ordering::Greater); + } + if self.release.is_none() && other.release.is_none() { + return None; + } + if self.release.is_none() && other.release.is_some() { + return Some(Ordering::Less); + } + if self.release.is_some() && other.release.is_none() { + return Some(Ordering::Greater); + } + let ord = match version_sort(self.version.as_ref().unwrap(), + other.version.as_ref().unwrap()) { + Ok(ord) => ord, + Err(e) => { + error!("This was a very bad version number: {:?}", e); + return None; + } + }; + match ord { + Ordering::Greater => return Some(Ordering::Greater), + Ordering::Less => return Some(Ordering::Less), + Ordering::Equal => { + return Some(self.release.cmp(&other.release)); + } + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum MetaFile { + CFlags, + Config, + Deps, + TDeps, + Exposes, + Ident, + LdRunPath, + LdFlags, + Manifest, + Path, +} + +impl fmt::Display for MetaFile { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let id = match *self { + MetaFile::CFlags => "CFLAGS", + MetaFile::Config => "default.toml", + MetaFile::Deps => "DEPS", + MetaFile::TDeps => "TDEPS", + MetaFile::Exposes => "EXPOSES", + MetaFile::Ident => "IDENT", + MetaFile::LdRunPath => "LD_RUN_PATH", + MetaFile::LdFlags => "LDFLAGS", + MetaFile::Manifest => "MANIFEST", + MetaFile::Path => "PATH", + }; + write!(f, "{}", id) + } +} + +type Metadata = HashMap; + +#[derive(Debug)] +pub struct PackageArchive { + pub path: PathBuf, + metadata: Option, +} + +impl PackageArchive { + pub fn new(path: PathBuf) -> Self { + PackageArchive { + path: path, + metadata: None, + } + } + + /// Calculate and return the checksum of the package archive in hexadecimal format. + /// + /// # Failures + /// + /// * If the archive cannot be read + pub fn checksum(&self) -> Result { + let mut digest = Sha256::new(); + let mut file = try!(File::open(&self.path)); + let mut buffer = Vec::new(); + try!(file.read_to_end(&mut buffer)); + digest.input(&buffer); + let hash = digest.result_str(); + Ok(hash) + } + + pub fn cflags(&mut self) -> Result> { + match self.read_metadata(MetaFile::CFlags) { + Ok(data) => Ok(data.cloned()), + Err(e) => Err(e), + } + } + + pub fn config(&mut self) -> Result> { + match self.read_metadata(MetaFile::Config) { + Ok(data) => Ok(data.cloned()), + Err(e) => Err(e), + } + } + + /// Returns a list of package identifiers representing the runtime package dependencies for + /// this archive. + /// + /// # Failures + /// + /// * If a `DEPS` metafile is not found in the archive + /// * If the archive cannot be read + /// * If the archive cannot be verified + pub fn deps(&mut self) -> Result> { + self.read_deps(MetaFile::Deps) + } + + /// Returns a list of package identifiers representing the transitive runtime package + /// dependencies for this archive. + /// + /// # Failures + /// + /// * If a `TDEPS` metafile is not found in the archive + /// * If the archive cannot be read + /// * If the archive cannot be verified + pub fn tdeps(&mut self) -> Result> { + self.read_deps(MetaFile::TDeps) + } + + pub fn exposes(&mut self) -> Result> { + match self.read_metadata(MetaFile::Exposes) { + Ok(Some(data)) => { + let ports: Vec = data.split(" ") + .filter_map(|port| port.parse::().ok()) + .collect(); + Ok(ports) + } + Ok(None) => Ok(vec![]), + Err(e) => Err(e), + } + } + + pub fn ident(&mut self) -> Result { + match self.read_metadata(MetaFile::Ident) { + Ok(None) => Err(Error::MetaFileMalformed(MetaFile::Ident)), + Ok(Some(data)) => PackageIdent::from_str(&data), + Err(e) => Err(e), + } + } + + pub fn ld_run_path(&mut self) -> Result> { + match self.read_metadata(MetaFile::LdRunPath) { + Ok(data) => Ok(data.cloned()), + Err(e) => Err(e), + } + } + + pub fn ldflags(&mut self) -> Result> { + match self.read_metadata(MetaFile::LdFlags) { + Ok(data) => Ok(data.cloned()), + Err(e) => Err(e), + } + } + + pub fn manifest(&mut self) -> Result { + match self.read_metadata(MetaFile::Manifest) { + Ok(None) => Err(Error::MetaFileMalformed(MetaFile::Manifest)), + Ok(Some(data)) => Ok(data.clone()), + Err(e) => Err(e), + } + } + + pub fn path(&mut self) -> Result> { + match self.read_metadata(MetaFile::Path) { + Ok(data) => Ok(data.cloned()), + Err(e) => Err(e), + } + } + + /// A plain string representation of the archive's file name. + pub fn file_name(&self) -> String { + self.path.file_name().unwrap().to_string_lossy().into_owned() + } + + /// Given a package name and a path to a file as an `&str`, verify + /// the files gpg signature. + /// + /// # Failures + /// + /// * Fails if it cannot verify the GPG signature for any reason + pub fn verify(&self) -> Result<()> { + match gpg::verify(self.path.to_str().unwrap()) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } + } + + /// Given a package name and a path to a file as an `&str`, unpack + /// the package. + /// + /// # Failures + /// + /// * If the package cannot be unpacked via gpg + pub fn unpack(&self) -> Result<()> { + let file = self.path.to_str().unwrap().to_string(); + let mut out = try!(gpg::verify(&file)); + try!(out.seek(SeekFrom::Start(0))); + let mut builder = reader::Builder::new(); + try!(builder.support_format(ReadFormat::All)); + try!(builder.support_filter(ReadFilter::All)); + let mut reader = try!(builder.open_stream(out)); + let writer = writer::Disk::new(); + try!(writer.set_standard_lookup()); + try!(writer.write(&mut reader, Some("/"))); + try!(writer.close()); + Ok(()) + } + + fn read_deps(&mut self, file: MetaFile) -> Result> { + let mut deps: Vec = vec![]; + match self.read_metadata(file) { + Ok(Some(body)) => { + let ids: Vec = body.split("\n").map(|d| d.to_string()).collect(); + for id in &ids { + let package = try!(PackageIdent::from_str(id)); + if !package.fully_qualified() { + // JW TODO: use a more appropriate erorr to describe the invalid + // user input here. (user because a package was generated by a user + // and read into program) + return Err(Error::InvalidPackageIdent(package.to_string())); + } + deps.push(package); + } + Ok(deps) + } + Ok(None) => Ok(vec![]), + Err(Error::MetaFileNotFound(_)) => Ok(deps), + Err(e) => Err(e), + } + } + + fn read_metadata(&mut self, file: MetaFile) -> Result> { + if let Some(ref files) = self.metadata { + return Ok(files.get(&file)); + } + + let mut metadata = Metadata::new(); + let mut matched_count = 0u8; + let f = self.path.to_str().unwrap().to_string(); + let mut out = try!(gpg::verify(&f)); + try!(out.seek(SeekFrom::Start(0))); + let mut builder = reader::Builder::new(); + try!(builder.support_format(ReadFormat::All)); + try!(builder.support_filter(ReadFilter::All)); + let mut reader = try!(builder.open_stream(out)); + loop { + let mut matched_type: Option = None; + if let Some(entry) = reader.next_header() { + for (matched, regx) in METAFILE_REGXS.iter() { + if regx.is_match(entry.pathname()) { + matched_type = Some((*matched).clone()); + matched_count += 1; + break; + } + } + } else { + break; + } + + if matched_type.is_none() { + continue; + } + + match reader.read_block() { + Ok(Some(bytes)) => { + match str::from_utf8(bytes) { + Ok(content) => { + metadata.insert(matched_type.unwrap(), content.trim().to_string()); + } + Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())), + } + } + Ok(None) => (), + Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())), + } + + if matched_count == METAFILE_REGXS.len() as u8 { + break; + } + } + self.metadata = Some(metadata); + Ok(self.metadata.as_ref().unwrap().get(&file)) + } +} + +/// Sorts two packages according to their version. +/// +/// We are a bit more strict than your average package management solution on versioning. +/// What we support is the "some number of digits or dots" (the version number), +/// followed by an optional "-" and any alphanumeric string (the extension). When determining sort order, we: +/// +/// * Separate the version numbers from the extensions +/// * Split the version numbers into an array of digits on any '.' characters. Digits are convered +/// into . +/// * Compare the version numbers by iterating over them. If 'a' is greater or lesser than 'b', we +/// return that as the result. If it is equal, we move to the next digit and repeat. If one of +/// the version numbers is exhausted before the other, it gains 0's for the missing slot. +/// * If the version numbers are equal, but either A or B has an extension (but not both) than the +/// version without the extension is greater. (1.0.0 is greater than 1.0.0-alpha6) +/// * If both have an extension, it is compared lexicographically, with the result as the final +/// ordering. +/// +/// Returns a Error if we fail to match for any reason. +pub fn version_sort(a_version: &str, b_version: &str) -> Result { + let (a_parts, a_extension) = try!(split_version(a_version)); + let (b_parts, b_extension) = try!(split_version(b_version)); + let mut a_iter = a_parts.iter(); + let mut b_iter = b_parts.iter(); + loop { + let mut a_exhausted = false; + let mut b_exhausted = false; + let a_num = match a_iter.next() { + Some(i) => try!(i.parse::()), + None => { + a_exhausted = true; + 0u64 + } + }; + let b_num = match b_iter.next() { + Some(i) => try!(i.parse::()), + None => { + b_exhausted = true; + 0u64 + } + }; + if a_exhausted && b_exhausted { + break; + } + match a_num.cmp(&b_num) { + Ordering::Greater => { + return Ok(Ordering::Greater); + } + Ordering::Equal => { + continue; + } + Ordering::Less => { + return Ok(Ordering::Less); + } + } + } + + // If you have equal digits, and one has an extension, it is + // the plain digits who win. + // 1.0.0-alpha1 vs 1.0.0 + if a_extension.is_some() && b_extension.is_none() { + return Ok(Ordering::Less); + } else if a_extension.is_none() && b_extension.is_some() { + return Ok(Ordering::Greater); + } else if a_extension.is_none() && b_extension.is_none() { + return Ok(Ordering::Equal); + } else { + let a = match a_extension { + Some(a) => a, + None => String::new(), + }; + let b = match b_extension { + Some(b) => b, + None => String::new(), + }; + return Ok(a.cmp(&b)); + } +} + +fn split_version(version: &str) -> Result<(Vec<&str>, Option)> { + let re = try!(Regex::new(r"([\d\.]+)(-.+)?")); + let caps = match re.captures(version) { + Some(caps) => caps, + None => return Err(Error::InvalidPackageIdent(version.to_string())), + }; + let version_number = caps.at(1).unwrap(); + let extension = match caps.at(2) { + Some(e) => { + let mut estr: String = e.to_string(); + estr.remove(0); + Some(estr) + } + None => None, + }; + let version_parts: Vec<&str> = version_number.split('.').collect(); + Ok((version_parts, extension)) +} + +#[cfg(test)] +mod tests { + use super::*; + use super::split_version; + use std::cmp::Ordering; + use std::cmp::PartialOrd; + + #[test] + fn package_ident_partial_eq() { + let a = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + let b = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + assert_eq!(a, b); + } + + #[test] + fn package_ident_partial_ord() { + let a = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.1".to_string()), + Some("20150521131555".to_string())); + let b = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + match a.partial_cmp(&b) { + Some(ord) => assert_eq!(ord, Ordering::Greater), + None => panic!("Ordering should be greater"), + } + } + + #[test] + fn package_ident_partial_ord_bad_name() { + let a = PackageIdent::new("bldr".to_string(), + "snoopy".to_string(), + Some("1.0.1".to_string()), + Some("20150521131555".to_string())); + let b = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + match a.partial_cmp(&b) { + Some(_) => panic!("We tried to return an order"), + None => assert!(true), + } + } + + #[test] + fn package_ident_partial_ord_different_origin() { + let a = PackageIdent::new("adam".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + let b = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + match a.partial_cmp(&b) { + Some(ord) => assert_eq!(ord, Ordering::Equal), + None => panic!("We failed to return an order"), + } + } + + #[test] + fn package_ident_partial_ord_release() { + let a = PackageIdent::new("adam".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131556".to_string())); + let b = PackageIdent::new("bldr".to_string(), + "bldr".to_string(), + Some("1.0.0".to_string()), + Some("20150521131555".to_string())); + match a.partial_cmp(&b) { + Some(ord) => assert_eq!(ord, Ordering::Greater), + None => panic!("We failed to return an order"), + } + } + + #[test] + fn split_version_returns_both_parts() { + let svr = split_version("1.2.3-beta16"); + match svr { + Ok((version_parts, Some(extension))) => { + assert_eq!(vec!["1", "2", "3"], version_parts); + assert_eq!("beta16", extension); + } + Ok((_, None)) => panic!("Has an extension"), + Err(e) => panic!("{:?}", e), + } + } + + #[test] + fn version_sort_simple() { + match version_sort("1.0.0", "2.0.0") { + Ok(compare) => assert_eq!(compare, Ordering::Less), + Err(e) => panic!("{:?}", e), + } + match version_sort("2.0.1", "2.0.0") { + Ok(compare) => assert_eq!(compare, Ordering::Greater), + Err(e) => panic!("{:?}", e), + } + match version_sort("2.1.1", "2.1.1") { + Ok(compare) => assert_eq!(compare, Ordering::Equal), + Err(e) => panic!("{:?}", e), + } + match version_sort("20150521131347", "20150521131346") { + Ok(compare) => assert_eq!(compare, Ordering::Greater), + Err(e) => panic!("{:?}", e), + } + } + + #[test] + fn version_sort_complex() { + match version_sort("1.0.0-alpha2", "1.0.0-alpha1") { + Ok(compare) => assert_eq!(compare, Ordering::Greater), + Err(e) => panic!("{:?}", e), + } + match version_sort("1.0.0-alpha1", "1.0.0-alpha2") { + Ok(compare) => assert_eq!(compare, Ordering::Less), + Err(e) => panic!("{:?}", e), + } + match version_sort("1.0.0-beta1", "1.0.0-alpha1000") { + Ok(compare) => assert_eq!(compare, Ordering::Greater), + Err(e) => panic!("{:?}", e), + } + match version_sort("2.1.1", "2.1.1-alpha2") { + Ok(compare) => assert_eq!(compare, Ordering::Greater), + Err(e) => panic!("{:?}", e), + } + match version_sort("2.1.1-alpha2", "2.1.1") { + Ok(compare) => assert_eq!(compare, Ordering::Less), + Err(e) => panic!("{:?}", e), + } + } + + #[test] + fn check_fully_qualified_package_id() { + let partial = PackageIdent::new("chef", "libarchive", None, None); + let full = PackageIdent::new("chef", "libarchive", Some("1.2.3"), Some("1234")); + assert!(!partial.fully_qualified()); + assert!(full.fully_qualified()); + } +} diff --git a/components/core/src/util/mod.rs b/components/core/src/util/mod.rs new file mode 100644 index 00000000000..deb56e33978 --- /dev/null +++ b/components/core/src/util/mod.rs @@ -0,0 +1,7 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +pub mod perm; diff --git a/components/bldr/src/util/perm.rs b/components/core/src/util/perm.rs similarity index 75% rename from components/bldr/src/util/perm.rs rename to components/core/src/util/perm.rs index c56899db750..e79a13f70fa 100644 --- a/components/bldr/src/util/perm.rs +++ b/components/core/src/util/perm.rs @@ -4,32 +4,31 @@ // this file ("Licensee") apply to Licensee's use of the Software until such time that the Software // is made available under an open source license such as the Apache 2.0 License. -use error::{BldrResult, ErrorKind}; use std::process::Command; -static LOGKEY: &'static str = "UP"; +use error::{Error, Result}; -pub fn set_owner(path: &str, owner: &str) -> BldrResult<()> { +pub fn set_owner(path: &str, owner: &str) -> Result<()> { let output = try!(Command::new("chown") .arg(owner) .arg(path) .output()); match output.status.success() { true => Ok(()), - false => Err(bldr_error!(ErrorKind::PermissionFailed)), + false => Err(Error::PermissionFailed), } } // When Rust stabilizes this interface, we can move to the cross // platform abstraction. Until then, if we move to Windows or some // other platform, this code will need to become platform specific. -pub fn set_permissions(path: &str, perm: &str) -> BldrResult<()> { +pub fn set_permissions(path: &str, perm: &str) -> Result<()> { let output = try!(Command::new("chmod") .arg(perm) .arg(path) .output()); match output.status.success() { true => Ok(()), - false => Err(bldr_error!(ErrorKind::PermissionFailed)), + false => Err(Error::PermissionFailed), } } diff --git a/components/depot-client/Cargo.lock b/components/depot-client/Cargo.lock new file mode 100644 index 00000000000..dccb6c75b78 --- /dev/null +++ b/components/depot-client/Cargo.lock @@ -0,0 +1,445 @@ +[root] +name = "bldr_depot_client" +version = "0.4.0" +dependencies = [ + "bldr_core 0.4.0", + "bldr_depot_core 0.4.0", + "hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bldr_core" +version = "0.4.0" +dependencies = [ + "gpgme 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libarchive 0.1.0", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.58 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bldr_depot_core" +version = "0.4.0" +dependencies = [ + "bldr_core 0.4.0", + "hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lmdb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cookie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gcc" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gdi32-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpg-error" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpg-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpgme-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hpack" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cookie 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libarchive" +version = "0.1.0" +dependencies = [ + "libarchive3-sys 0.1.0", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libarchive3-sys" +version = "0.1.0" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libgpg-error-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libressl-pnacl-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lmdb-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys-extras 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys-extras" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pnacl-build-helper" +version = "1.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rust-crypto" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "solicit" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempdir" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "traitobject" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "url" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "user32-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uuid" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/components/depot-client/Cargo.toml b/components/depot-client/Cargo.toml new file mode 100644 index 00000000000..55090df8b53 --- /dev/null +++ b/components/depot-client/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bldr_depot_client" +version = "0.4.0" +authors = ["Adam Jacob ", "Jamie Winsor ", "Fletcher Nichol ", "Joshua Timberman ", "Dave Parfitt "] + +[dependencies] +hyper = "*" +log = "*" +rustc-serialize = "*" + +[dependencies.bldr_core] +path = "../core" + +[dependencies.bldr_depot_core] +path = "../depot-core" diff --git a/components/depot-client/src/error.rs b/components/depot-client/src/error.rs new file mode 100644 index 00000000000..0985dacdf87 --- /dev/null +++ b/components/depot-client/src/error.rs @@ -0,0 +1,86 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::error; +use std::io; +use std::fmt; +use std::result; + +use hyper; + +use bldr::{self, package}; + +#[derive(Debug)] +pub enum Error { + BldrCore(bldr::Error), + HTTP(hyper::status::StatusCode), + HyperError(hyper::error::Error), + IO(io::Error), + NoFilePart, + NoXFilename, + RemotePackageNotFound(package::PackageIdent), + WriteSyncFailed, +} + +pub type Result = result::Result; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match *self { + Error::BldrCore(ref e) => format!("{}", e), + Error::HTTP(ref e) => format!("{}", e), + Error::HyperError(ref err) => format!("{}", err), + Error::IO(ref e) => format!("{}", e), + Error::NoFilePart => { + format!("An invalid path was passed - we needed a filename, and this path does \ + not have one") + } + Error::NoXFilename => format!("Invalid download from a Depot - missing X-Filename header"), + Error::RemotePackageNotFound(ref pkg) => { + if pkg.fully_qualified() { + format!("Cannot find package in any sources: {}", pkg) + } else { + format!("Cannot find a release of package in any sources: {}", pkg) + } + } + Error::WriteSyncFailed => format!("Could not write to destination; perhaps the disk is full?"), + }; + write!(f, "{}", msg) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::BldrCore(ref err) => err.description(), + Error::HTTP(_) => "Received an HTTP error", + Error::HyperError(ref err) => err.description(), + Error::IO(ref err) => err.description(), + Error::NoFilePart => "An invalid path was passed - we needed a filename, and this path does not have one", + Error::NoXFilename => "Invalid download from a Depot - missing X-Filename header", + Error::RemotePackageNotFound(_) => "Cannot find a package in any sources", + Error::WriteSyncFailed => "Could not write to destination; bytes written was 0 on a non-0 buffer", + } + } +} + +impl From for Error { + fn from(err: bldr::Error) -> Error { + Error::BldrCore(err) + } +} + +impl From for Error { + fn from(err: hyper::error::Error) -> Error { + Error::HyperError(err) + } +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IO(err) + } +} diff --git a/components/bldr/src/depot/client.rs b/components/depot-client/src/lib.rs similarity index 83% rename from components/bldr/src/depot/client.rs rename to components/depot-client/src/lib.rs index 775565dca6b..525fc7c16c1 100644 --- a/components/bldr/src/depot/client.rs +++ b/components/depot-client/src/lib.rs @@ -4,21 +4,28 @@ // this file ("Licensee") apply to Licensee's use of the Software until such time that the Software // is made available under an open source license such as the Apache 2.0 License. +extern crate bldr_core as bldr; +extern crate bldr_depot_core as depot_core; +#[macro_use] +extern crate hyper; +#[macro_use] +extern crate log; +extern crate rustc_serialize; + +pub mod error; + +pub use error::{Error, Result}; + use std::fs::{self, File}; use std::io::{Read, Write, BufWriter, Seek, SeekFrom}; use std::path::{Path, PathBuf}; -use hyper; +use bldr::package::{PackageArchive, PackageIdent}; +use depot_core::{XFileName, data_object}; use hyper::client::{Client, Body}; use hyper::status::StatusCode; use rustc_serialize::json; -use super::{XFileName, data_object}; -use error::{BldrResult, BldrError, ErrorKind}; -use package::{PackageArchive, PackageIdent}; - -static LOGKEY: &'static str = "RC"; - /// Download a public key from a remote Depot to the given filepath. /// /// # Failures @@ -26,7 +33,7 @@ static LOGKEY: &'static str = "RC"; /// * Key cannot be found /// * Remote Depot is not available /// * File cannot be created and written to -pub fn fetch_key(depot: &str, key: &str, path: &str) -> BldrResult { +pub fn fetch_key(depot: &str, key: &str, path: &str) -> Result { let url = format!("{}/keys/{}", depot, key); download(key, &url, path) } @@ -46,15 +53,15 @@ pub fn fetch_key(depot: &str, key: &str, path: &str) -> BldrResult { pub fn fetch_package(depot: &str, package: &PackageIdent, store: &str) - -> BldrResult { + -> Result { let url = format!("{}/pkgs/{}/download", depot, package); match download(&package.name, &url, store) { Ok(file) => { let path = PathBuf::from(file); Ok(PackageArchive::new(path)) } - Err(BldrError { err: ErrorKind::HTTP(StatusCode::NotFound), ..}) => { - Err(bldr_error!(ErrorKind::RemotePackageNotFound(package.clone()))) + Err(Error::HTTP(StatusCode::NotFound)) => { + Err(Error::RemotePackageNotFound(package.clone())) } Err(e) => Err(e), } @@ -69,14 +76,14 @@ pub fn fetch_package(depot: &str, /// /// * Package cannot be found /// * Remote Depot is not available -pub fn show_package(depot: &str, ident: &PackageIdent) -> BldrResult { +pub fn show_package(depot: &str, ident: &PackageIdent) -> Result { let url = url_show_package(depot, ident); let client = Client::new(); let request = client.get(&url); let mut res = try!(request.send()); if res.status != hyper::status::StatusCode::Ok { - return Err(bldr_error!(ErrorKind::RemotePackageNotFound(ident.clone()))); + return Err(Error::RemotePackageNotFound(ident.clone())); } let mut encoded = String::new(); @@ -92,9 +99,9 @@ pub fn show_package(depot: &str, ident: &PackageIdent) -> BldrResult BldrResult<()> { +pub fn put_key(depot: &str, path: &Path) -> Result<()> { let mut file = try!(File::open(path)); - let file_name = try!(path.file_name().ok_or(bldr_error!(ErrorKind::NoFilePart))); + let file_name = try!(path.file_name().ok_or(Error::NoFilePart)); let url = format!("{}/keys/{}", depot, file_name.to_string_lossy()); upload(&url, &mut file) } @@ -105,7 +112,7 @@ pub fn put_key(depot: &str, path: &Path) -> BldrResult<()> { /// /// * Remote Depot is not available /// * File cannot be read -pub fn put_package(depot: &str, pa: &mut PackageArchive) -> BldrResult<()> { +pub fn put_package(depot: &str, pa: &mut PackageArchive) -> Result<()> { let checksum = try!(pa.checksum()); let ident = try!(pa.ident()); let url = format!("{}/pkgs/{}?checksum={}", depot, ident, checksum); @@ -121,19 +128,19 @@ fn url_show_package(depot: &str, package: &PackageIdent) -> String { } } -fn download(status: &str, url: &str, path: &str) -> BldrResult { +fn download(status: &str, url: &str, path: &str) -> Result { debug!("Making request to url {}", url); let client = Client::new(); let mut res = try!(client.get(url).send()); debug!("Response: {:?}", res); if res.status != hyper::status::StatusCode::Ok { - return Err(bldr_error!(ErrorKind::HTTP(res.status))); + return Err(Error::HTTP(res.status)); } let file_name = match res.headers.get::() { Some(filename) => format!("{}", filename), - None => return Err(bldr_error!(ErrorKind::NoXFilename)), + None => return Err(Error::NoXFilename), }; let length = res.headers .get::() @@ -173,7 +180,7 @@ fn download(status: &str, url: &str, path: &str) -> BldrResult { // Write the buffer to the BufWriter on the Heap let bytes_written = try!(writer.write(&buf[0..len])); if bytes_written == 0 { - return Err(bldr_error!(ErrorKind::WriteSyncFailed)); + return Err(Error::WriteSyncFailed); } written = written + (bytes_written as i64); progress(status, written, &length, false); @@ -184,7 +191,7 @@ fn download(status: &str, url: &str, path: &str) -> BldrResult { Ok(finalfile) } -fn upload(url: &str, file: &mut File) -> BldrResult<()> { +fn upload(url: &str, file: &mut File) -> Result<()> { debug!("Uploading to {}", url); try!(file.seek(SeekFrom::Start(0))); let client = Client::new(); @@ -194,12 +201,12 @@ fn upload(url: &str, file: &mut File) -> BldrResult<()> { Ok(()) } else { debug!("Response {:?}", response); - Err(bldr_error!(ErrorKind::HTTP(response.status))) + Err(Error::HTTP(response.status)) } } fn progress(status: &str, written: i64, length: &str, finished: bool) { - let progress = output_format!(preamble status, "{}/{}", written, length); + let progress = format!("{} {}/{}", status, written, length); print!("{}", from_char(progress.len(), '\x08')); if finished { println!("{}", progress); diff --git a/components/depot-core/Cargo.lock b/components/depot-core/Cargo.lock new file mode 100644 index 00000000000..5b91e75c5ff --- /dev/null +++ b/components/depot-core/Cargo.lock @@ -0,0 +1,434 @@ +[root] +name = "bldr_depot_core" +version = "0.4.0" +dependencies = [ + "bldr_core 0.4.0", + "hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lmdb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bldr_core" +version = "0.4.0" +dependencies = [ + "gpgme 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libarchive 0.1.0", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.58 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cookie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gcc" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gdi32-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpg-error" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpg-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpgme-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hpack" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cookie 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libarchive" +version = "0.1.0" +dependencies = [ + "libarchive3-sys 0.1.0", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libarchive3-sys" +version = "0.1.0" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libgpg-error-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libressl-pnacl-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lmdb-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys-extras 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys-extras" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pnacl-build-helper" +version = "1.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rust-crypto" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "solicit" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempdir" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "traitobject" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "url" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "user32-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uuid" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/components/depot-core/Cargo.toml b/components/depot-core/Cargo.toml new file mode 100644 index 00000000000..eafe7d6701f --- /dev/null +++ b/components/depot-core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "bldr_depot_core" +version = "0.4.0" +authors = ["Adam Jacob ", "Jamie Winsor ", "Fletcher Nichol ", "Joshua Timberman ", "Dave Parfitt "] + +[dependencies] +hyper = "*" +libc = "*" +lmdb-sys = "*" +rustc-serialize = "*" + +[dependencies.bldr_core] +path = "../core" diff --git a/components/bldr/src/depot/data_object.rs b/components/depot-core/src/data_object.rs similarity index 83% rename from components/bldr/src/depot/data_object.rs rename to components/depot-core/src/data_object.rs index 03d82b8bbfe..ec09413a609 100644 --- a/components/bldr/src/depot/data_object.rs +++ b/components/depot-core/src/data_object.rs @@ -5,15 +5,14 @@ // is made available under an open source license such as the Apache 2.0 License. use std::fmt; +use std::result; +use std::slice; +use bldr::{package, Error, Result}; +use libc::c_void; +use lmdb_sys; use rustc_serialize::{Encoder, Decoder, Encodable, Decodable}; -use error::{BldrResult, ErrorKind}; -use package; -use super::data_store::{FromMdbValue, ToMdbValue}; - -static LOGKEY: &'static str = "DO"; - pub trait DataObject : Encodable + Decodable { type Key: ToMdbValue + FromMdbValue + fmt::Display; fn ident(&self) -> &Self::Key; @@ -83,7 +82,7 @@ impl AsRef for PackageIdent { } impl Encodable for PackageIdent { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { + fn encode(&self, s: &mut S) -> result::Result<(), S::Error> { try!(s.emit_struct("PackageIdent", self.len() as usize, |s| { try!(s.emit_struct_field("origin", 0, |s| self.0.origin.encode(s))); try!(s.emit_struct_field("name", 1, |s| self.0.name.encode(s))); @@ -100,7 +99,7 @@ impl Encodable for PackageIdent { } impl Decodable for PackageIdent { - fn decode(d: &mut D) -> Result { + fn decode(d: &mut D) -> result::Result { d.read_struct("PackageIdent", 4, |d| { let origin: String = try!(d.read_struct_field("origin", 0, |d| Decodable::decode(d))); let name: String = try!(d.read_struct_field("name", 1, |d| Decodable::decode(d))); @@ -173,15 +172,15 @@ pub struct Package { } impl Package { - pub fn from_archive(archive: &mut package::PackageArchive) -> BldrResult { + pub fn from_archive(archive: &mut package::PackageArchive) -> Result { let ident = match archive.ident() { Ok(value) => { if !value.fully_qualified() { - return Err(bldr_error!(ErrorKind::InvalidPackageIdent(value.to_string()))); + return Err(Error::InvalidPackageIdent(value.to_string())); } PackageIdent::new(value) } - Err(e) => return Err(e), + Err(e) => return Err(Error::from(e)), }; Ok(Package { ident: ident, @@ -195,19 +194,6 @@ impl Package { } } -impl Into for Package { - fn into(self) -> package::Package { - package::Package { - origin: self.ident.0.origin, - name: self.ident.0.name, - version: self.ident.0.version.unwrap(), - release: self.ident.0.release.unwrap(), - deps: self.deps.into_iter().map(|d| d.into()).collect(), - tdeps: self.tdeps.into_iter().map(|d| d.into()).collect(), - } - } -} - impl DataObject for Package { type Key = String; @@ -215,3 +201,29 @@ impl DataObject for Package { &self.ident.1 } } + +pub unsafe trait ToMdbValue { + fn to_mdb_value(&self) -> lmdb_sys::MDB_val; +} + +unsafe impl ToMdbValue for String { + fn to_mdb_value(&self) -> lmdb_sys::MDB_val { + let t: &str = self; + lmdb_sys::MDB_val { + mv_data: t.as_ptr() as *mut c_void, + mv_size: t.len(), + } + } +} + +pub unsafe trait FromMdbValue { + unsafe fn from_mdb_value(value: &lmdb_sys::MDB_val) -> Self; +} + +unsafe impl FromMdbValue for String { + unsafe fn from_mdb_value(value: &lmdb_sys::MDB_val) -> Self { + let bytes: Vec = slice::from_raw_parts(value.mv_data as *const u8, value.mv_size) + .to_vec(); + String::from_utf8(bytes).unwrap() + } +} diff --git a/components/depot-core/src/lib.rs b/components/depot-core/src/lib.rs new file mode 100644 index 00000000000..c51619e8803 --- /dev/null +++ b/components/depot-core/src/lib.rs @@ -0,0 +1,17 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +extern crate bldr_core as bldr; +#[macro_use] +extern crate hyper; +extern crate libc; +extern crate lmdb_sys; +extern crate rustc_serialize; + +pub mod data_object; + +header! { (XFileName, "X-Filename") => [String] } +header! { (ETag, "ETag") => [String] } diff --git a/components/depot/Cargo.lock b/components/depot/Cargo.lock new file mode 100644 index 00000000000..968d79ca5b8 --- /dev/null +++ b/components/depot/Cargo.lock @@ -0,0 +1,695 @@ +[root] +name = "bldr_depot" +version = "0.4.0" +dependencies = [ + "bincode 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bldr_core 0.4.0", + "bldr_depot_core 0.4.0", + "clap 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gpgme 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libarchive 0.1.0", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lmdb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "router 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "urlencoded 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bincode" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bldr_core" +version = "0.4.0" +dependencies = [ + "gpgme 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libarchive 0.1.0", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bldr_depot_core" +version = "0.4.0" +dependencies = [ + "bldr_core 0.4.0", + "hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lmdb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bodyparser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "persistent 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "conduit-mime-types" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cookie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "env_logger" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gcc" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gdi32-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpg-error" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpg-error 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gpgme-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gpgme-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libgpg-error-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hpack" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cookie 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iron" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libarchive" +version = "0.1.0" +dependencies = [ + "libarchive3-sys 0.1.0", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libarchive3-sys" +version = "0.1.0" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libgpg-error-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libressl-pnacl-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lmdb-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "modifier" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys-extras 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys-extras" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "persistent" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plugin" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pnacl-build-helper" +version = "1.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "route-recognizer" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "router" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "route-recognizer 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust-crypto" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "solicit" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tempdir" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "traitobject" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "traitobject" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unsafe-any 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unsafe-any" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "urlencoded" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bodyparser 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "user32-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uuid" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vec_map" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/components/depot/Cargo.toml b/components/depot/Cargo.toml new file mode 100644 index 00000000000..3571ec91035 --- /dev/null +++ b/components/depot/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "bldr_depot" +version = "0.4.0" +authors = ["Adam Jacob ", "Jamie Winsor ", "Fletcher Nichol ", "Joshua Timberman ", "Dave Parfitt "] + +[[bin]] +name = "bldr-depot" +doc = false + +[dependencies] +bincode = "*" +bitflags = "*" +env_logger = "*" +gpgme = "*" +hyper = "*" +iron = "*" +libc = "*" +lazy_static = "*" +lmdb-sys = "*" +log = "*" +openssl = "*" +regex = "*" +router = "*" +rust-crypto = "*" +rustc-serialize = "*" +time = "*" +toml = "*" +urlencoded = "*" +walkdir = "*" + +[dependencies.clap] +features = [ "suggestions", "color", "unstable" ] + +[dependencies.bldr_core] +path = "../core" + +[dependencies.bldr_depot_core] +path = "../depot-core" + +[dependencies.libarchive] +path = "../../vendor/libarchive-rust" + +[dev-dependencies] +url = "*" +uuid = "*" diff --git a/components/depot/src/config.rs b/components/depot/src/config.rs new file mode 100644 index 00000000000..6353ac4ad0a --- /dev/null +++ b/components/depot/src/config.rs @@ -0,0 +1,25 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::net; + +#[derive(Default, Debug, PartialEq, Eq)] +pub struct Config { + pub path: String, + pub listen_addr: super::ListenAddr, + pub port: super::ListenPort, +} + +impl Config { + /// Create a default `Config` + pub fn new() -> Config { + Config::default() + } + + pub fn depot_addr(&self) -> net::SocketAddrV4 { + net::SocketAddrV4::new(self.listen_addr.0.clone(), self.port.0.clone()) + } +} diff --git a/components/bldr/src/depot/data_store.rs b/components/depot/src/data_store.rs similarity index 92% rename from components/bldr/src/depot/data_store.rs rename to components/depot/src/data_store.rs index 0500661b20e..e181df376c6 100644 --- a/components/bldr/src/depot/data_store.rs +++ b/components/depot/src/data_store.rs @@ -17,14 +17,12 @@ use std::sync::{Arc, Mutex}; use bincode::{self, SizeLimit}; use bincode::rustc_serialize::{encode, decode}; +use depot_core::data_object::{self, DataObject, ToMdbValue, FromMdbValue}; use libc::{c_void, c_int, c_uint, mode_t, size_t}; use lmdb_sys; use rustc_serialize::{Encodable, Decodable}; -use error::{BldrResult, ErrorKind}; -use super::data_object::{self, DataObject}; - -static LOGKEY: &'static str = "DS"; +use error::{Error, Result}; lazy_static! { static ref OPEN_LOCK: Mutex<()> = Mutex::new(()); @@ -100,7 +98,7 @@ macro_rules! try_mdb { let code = $e; match code { lmdb_sys::MDB_SUCCESS => (), - _ => return Err(bldr_error!(ErrorKind::MdbError(MdbError::from(code)))) + _ => return Err(Error::from(MdbError::from(code))) } } ) @@ -112,7 +110,7 @@ macro_rules! handle_mdb { let code = $e; match code { lmdb_sys::MDB_SUCCESS => Ok(()), - _ => Err(bldr_error!(ErrorKind::MdbError(MdbError::from(code)))) + _ => Err(Error::from(MdbError::from(code))) } } ) @@ -126,7 +124,7 @@ macro_rules! assert_txn_state_eq { if c == e { () } else { - return Err(bldr_error!(ErrorKind::MdbError(MdbError::StateError(c, e)))) + return Err(Error::from(MdbError::StateError(c, e))) } } ) @@ -247,7 +245,7 @@ impl From for MdbError { fn create_txn(env: &Environment, flags: c_uint, parent: Option<&mut lmdb_sys::MDB_txn>) - -> BldrResult<*mut lmdb_sys::MDB_txn> { + -> Result<*mut lmdb_sys::MDB_txn> { let mut handle: *mut lmdb_sys::MDB_txn = ptr::null_mut(); let parent = if parent.is_some() { parent.unwrap() as *mut lmdb_sys::MDB_txn @@ -264,7 +262,7 @@ fn cursor_get(cursor: *mut lmdb_sys::MDB_cursor, key: Option<&K>, value: Option<&D>, op: CursorOp) - -> BldrResult<(Option, D)> + -> Result<(Option, D)> where K: FromMdbValue + ToMdbValue, D: Encodable + Decodable { @@ -286,7 +284,7 @@ fn cursor_get(cursor: *mut lmdb_sys::MDB_cursor, let bytes: &[u8] = slice::from_raw_parts(dval.mv_data as *const u8, dval.mv_size); match decode(bytes) { Ok(dout) => Ok((kout, dout)), - Err(e) => Err(bldr_error!(ErrorKind::MdbError(MdbError::from(e)))), + Err(e) => Err(Error::from(MdbError::from(e))), } } } @@ -329,32 +327,6 @@ unsafe fn encoded_val_for(data: Option<&T>) -> lmdb_sys::MDB_val { } } -pub unsafe trait ToMdbValue { - fn to_mdb_value(&self) -> lmdb_sys::MDB_val; -} - -unsafe impl ToMdbValue for String { - fn to_mdb_value(&self) -> lmdb_sys::MDB_val { - let t: &str = self; - lmdb_sys::MDB_val { - mv_data: t.as_ptr() as *mut c_void, - mv_size: t.len(), - } - } -} - -pub unsafe trait FromMdbValue { - unsafe fn from_mdb_value(value: &lmdb_sys::MDB_val) -> Self; -} - -unsafe impl FromMdbValue for String { - unsafe fn from_mdb_value(value: &lmdb_sys::MDB_val) -> Self { - let bytes: Vec = slice::from_raw_parts(value.mv_data as *const u8, value.mv_size) - .to_vec(); - String::from_utf8(bytes).unwrap() - } -} - pub struct DataStore { pub packages: PkgDatabase, pub views: ViewDatabase, @@ -370,12 +342,12 @@ impl DataStore { /// * Cannot read/write to the given path /// * Cannot obtain a lock to create the environment /// * Could not create the environment or any of it's databases - pub fn open(path: &Path) -> BldrResult { + pub fn open(path: &Path) -> Result { let mut flags = EnvCreateFlags::empty(); flags.toggle(ENV_CREATE_NO_SUB_DIR); match path.parent() { Some(root) => try!(fs::create_dir_all(root)), - None => return Err(bldr_error!(ErrorKind::DbInvalidPath)), + None => return Err(Error::DbInvalidPath), } let env = try!(Environment::new() .map_size(1073741824) @@ -400,7 +372,7 @@ impl DataStore { /// /// * If a read-write transaction could not be acquired for any of the databases in the /// datastore - pub fn clear(&self) -> BldrResult<()> { + pub fn clear(&self) -> Result<()> { let txn = try!(self.packages.txn_rw()); try!(self.packages.clear(&txn)); try!(txn.commit()); @@ -460,7 +432,7 @@ impl EnvironmentBuilder { } /// Create/open the database - pub fn open(self, path: &Path, permissions: u32) -> BldrResult { + pub fn open(self, path: &Path, permissions: u32) -> Result { let handle: *mut lmdb_sys::MDB_env = ptr::null_mut(); unsafe { try_mdb!(lmdb_sys::mdb_env_create(mem::transmute(&handle))); @@ -483,10 +455,8 @@ impl EnvironmentBuilder { // JW TODO: if read only flag is set, lets return a read only environment. unsafe { - let path_str = try!(path.to_str().ok_or(bldr_error!(ErrorKind::DbInvalidPath))); - let path_ptr = try!(CString::new(path_str) - .map_err(|_| bldr_error!(ErrorKind::DbInvalidPath))) - .as_ptr(); + let path_str = try!(path.to_str().ok_or(Error::DbInvalidPath)); + let path_ptr = try!(CString::new(path_str).map_err(|_| Error::DbInvalidPath)).as_ptr(); match lmdb_sys::mdb_env_open(handle, path_ptr, @@ -495,7 +465,7 @@ impl EnvironmentBuilder { lmdb_sys::MDB_SUCCESS => Ok(Environment { handle: handle }), code => { lmdb_sys::mdb_env_close(handle); - Err(bldr_error!(ErrorKind::MdbError(MdbError::from(code)))) + Err(Error::from(MdbError::from(code))) } } } @@ -626,7 +596,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases that support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn dup_count(&self) -> BldrResult { + fn dup_count(&self) -> Result { assert_txn_state_eq!(self.state(), TxnState::Normal); let mut count: size_t = 0; unsafe { @@ -636,7 +606,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { } /// Position cursor at the first key/data item and return the data for the item. - fn first(&mut self) -> BldrResult<(::Key, D::Object)> { + fn first(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -652,7 +622,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases that support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn first_dup(&mut self) -> BldrResult { + fn first_dup(&mut self) -> Result { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -664,7 +634,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { } /// Position the cursor at the last key/data item and return the data for that item. - fn last(&mut self) -> BldrResult { + fn last(&mut self) -> Result { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -679,7 +649,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases that support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn last_dup(&mut self) -> BldrResult { + fn last_dup(&mut self) -> Result { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -691,7 +661,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { } /// Position the cursor at the next data item. - fn next(&mut self) -> BldrResult<(::Key, D::Object)> { + fn next(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -707,7 +677,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases taht support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn next_dup(&mut self) -> BldrResult<(::Key, D::Object)> { + fn next_dup(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -723,7 +693,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases taht support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn next_nodup(&mut self) -> BldrResult<(::Key, D::Object)> { + fn next_nodup(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -736,7 +706,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { } /// Position the cursor at the previous data item. - fn prev(&mut self) -> BldrResult<(::Key, D::Object)> { + fn prev(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -752,7 +722,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases taht support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn prev_dup(&mut self) -> BldrResult<(::Key, D::Object)> { + fn prev_dup(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -768,7 +738,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// /// This call is only valid on databases taht support sorted duplicate items by the /// `DB_ALLOW_DUPS` flag. - fn prev_nodup(&mut self) -> BldrResult<(::Key, D::Object)> { + fn prev_nodup(&mut self) -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), None, @@ -783,7 +753,7 @@ pub trait Cursor<'a, 'd, D: 'a + 'd + Database, T: Transaction<'a, D>> { /// Position the cursor at the specified key and return the key and data. fn set_key(&mut self, key: &::Key) - -> BldrResult<(::Key, D::Object)> { + -> Result<(::Key, D::Object)> { assert_txn_state_eq!(self.state(), TxnState::Normal); match cursor_get::<::Key, D::Object>(self.handle(), Some(key), @@ -807,7 +777,7 @@ impl<'a, D, T> RoCursor<'a, D, T> where D: Database, T: Transaction<'a, D> { - fn open(txn: &'a T) -> BldrResult { + fn open(txn: &'a T) -> Result { assert_txn_state_eq!(txn.state(), TxnState::Normal); let mut cursor: *mut lmdb_sys::MDB_cursor = ptr::null_mut(); unsafe { @@ -863,7 +833,7 @@ pub struct RwCursor<'a, D: 'a + Database> { } impl<'a, D: Database> RwCursor<'a, D> { - fn open(txn: &'a RwTransaction) -> BldrResult { + fn open(txn: &'a RwTransaction) -> Result { assert_txn_state_eq!(txn.state(), TxnState::Normal); let mut cursor: *mut lmdb_sys::MDB_cursor = ptr::null_mut(); unsafe { @@ -945,14 +915,14 @@ pub struct DatabaseBuilder { impl DatabaseBuilder { /// Create a new database - pub fn create(mut self, env: Arc) -> BldrResult { + pub fn create(mut self, env: Arc) -> Result { self.flags = self.flags | DB_CREATE; let handle = try!(self.open_database(&env)); T::open(env, handle) } /// Open an existing database - pub fn open(mut self, env: Arc) -> BldrResult { + pub fn open(mut self, env: Arc) -> Result { self.flags = self.flags - DB_CREATE; let handle = try!(self.open_database(&env)); T::open(env, handle) @@ -982,7 +952,7 @@ impl DatabaseBuilder { self } - fn open_database(&self, env: &Environment) -> BldrResult { + fn open_database(&self, env: &Environment) -> Result { match OPEN_LOCK.lock() { Ok(_) => { let name_ptr = if self.name.is_some() { @@ -1018,18 +988,18 @@ pub trait Database : Sized { type Object: DataObject + Any; /// Open the database - fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> BldrResult; + fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> Result; /// Returns the name of the database if one was provided fn name() -> &'static str; /// Drop all entries from the database - fn clear<'a, T: Transaction<'a, Self>>(&'a self, txn: &'a T) -> BldrResult<()> { + fn clear<'a, T: Transaction<'a, Self>>(&'a self, txn: &'a T) -> Result<()> { txn.clear() } /// Write an object into the database - fn write<'a>(&self, txn: &'a RwTransaction<'a, Self>, object: &Self::Object) -> BldrResult<()> { + fn write<'a>(&self, txn: &'a RwTransaction<'a, Self>, object: &Self::Object) -> Result<()> { txn.put(object.ident(), object) } @@ -1040,7 +1010,7 @@ pub trait Database : Sized { fn handle(&self) -> lmdb_sys::MDB_dbi; /// Retrieve statistics for the database - fn stat<'a, T: Transaction<'a, Self>>(&'a self, txn: &T) -> BldrResult { + fn stat<'a, T: Transaction<'a, Self>>(&'a self, txn: &T) -> Result { unsafe { let mut native: lmdb_sys::MDB_stat = mem::zeroed(); try_mdb!(lmdb_sys::mdb_stat(txn.handle(), self.handle(), &mut native)); @@ -1049,12 +1019,12 @@ pub trait Database : Sized { } /// Begin a read-only transaction - fn txn_ro<'b>(&'b self) -> BldrResult> { + fn txn_ro<'b>(&'b self) -> Result> { RoTransaction::begin(self) } /// Begin a read-write transaction - fn txn_rw<'b>(&'b self) -> BldrResult> { + fn txn_rw<'b>(&'b self) -> Result> { RwTransaction::begin(self) } } @@ -1062,13 +1032,13 @@ pub trait Database : Sized { /// Common behaviour for transactions pub trait Transaction<'a, D: 'a + Database> : Sized { /// Begin a transaction - fn begin(database: &'a D) -> BldrResult; + fn begin(database: &'a D) -> Result; /// Abandon all the operations of the transaction instead of saving them. fn abort(self) -> (); /// Commit all the operations of a transaction into the database. - fn commit(self) -> BldrResult<()>; + fn commit(self) -> Result<()>; /// Return a reference to the transaction's database fn database(&self) -> &'a D; @@ -1080,13 +1050,13 @@ pub trait Transaction<'a, D: 'a + Database> : Sized { fn state(&self) -> &TxnState; /// Returns a read-only cursor - fn cursor_ro(&'a self) -> BldrResult> { + fn cursor_ro(&'a self) -> Result> { RoCursor::open(self) } /// Begins a read-only nested transaction within the current transaction into the given /// database. - fn new_child(&'a self, database: &'a D2) -> BldrResult> { + fn new_child(&'a self, database: &'a D2) -> Result> { let handle = try!(create_txn(database.env(), lmdb_sys::MDB_RDONLY, Some(self.handle()))); Ok(RoTransaction { database: database, @@ -1098,7 +1068,7 @@ pub trait Transaction<'a, D: 'a + Database> : Sized { /// Begins a read-write nested transaction within the current transaction into the given /// database. - fn new_child_rw(&'a self, database: &'a D2) -> BldrResult> { + fn new_child_rw(&'a self, database: &'a D2) -> Result> { let handle = try!(create_txn(database.env(), 0, Some(self.handle()))); Ok(RwTransaction { database: database, @@ -1109,7 +1079,7 @@ pub trait Transaction<'a, D: 'a + Database> : Sized { } /// Clear all data in the database. - fn clear(&self) -> BldrResult<()> { + fn clear(&self) -> Result<()> { assert_txn_state_eq!(self.state(), TxnState::Normal); unsafe { try_mdb!(lmdb_sys::mdb_drop(self.handle(), self.database().handle(), 0)); @@ -1118,7 +1088,7 @@ pub trait Transaction<'a, D: 'a + Database> : Sized { } /// Return a value from the database. - fn get(&self, k: &::Key) -> BldrResult { + fn get(&self, k: &::Key) -> Result { assert_txn_state_eq!(self.state(), TxnState::Normal); unsafe { let mut key = k.to_mdb_value(); @@ -1130,7 +1100,7 @@ pub trait Transaction<'a, D: 'a + Database> : Sized { let bytes: &[u8] = slice::from_raw_parts(data.mv_data as *const u8, data.mv_size); match decode(bytes) { Ok(value) => Ok(value), - Err(e) => Err(bldr_error!(ErrorKind::MdbError(MdbError::from(e)))), + Err(e) => Err(Error::from(MdbError::from(e))), } } } @@ -1159,7 +1129,7 @@ impl<'a, D: Database> RoTransaction<'a, D> { /// Acquires a new reader lock for a transaction that had been previously released by `renew()`. #[allow(dead_code)] - fn renew(&mut self) -> BldrResult<()> { + fn renew(&mut self) -> Result<()> { assert_txn_state_eq!(self.state(), TxnState::Released); unsafe { try_mdb!(lmdb_sys::mdb_txn_renew(self.handle())); @@ -1170,7 +1140,7 @@ impl<'a, D: Database> RoTransaction<'a, D> { } impl<'a, D: 'a + Database> Transaction<'a, D> for RoTransaction<'a, D> { - fn begin(database: &'a D) -> BldrResult { + fn begin(database: &'a D) -> Result { let handle = try!(create_txn(database.env(), lmdb_sys::MDB_RDONLY, None)); Ok(RoTransaction { database: database, @@ -1189,7 +1159,7 @@ impl<'a, D: 'a + Database> Transaction<'a, D> for RoTransaction<'a, D> { } } - fn commit(mut self) -> BldrResult<()> { + fn commit(mut self) -> Result<()> { assert_txn_state_eq!(self.state(), TxnState::Normal); unsafe { try_mdb!(lmdb_sys::mdb_txn_commit(self.handle())); @@ -1232,7 +1202,7 @@ pub struct RwTransaction<'a, D: 'a + Database> { impl<'a, D: Database> RwTransaction<'a, D> { /// Returns a read-write cursor - pub fn cursor_rw(&'a self) -> BldrResult> { + pub fn cursor_rw(&'a self) -> Result> { RwCursor::open(self) } @@ -1243,7 +1213,7 @@ impl<'a, D: Database> RwTransaction<'a, D> { /// allowed. /// /// Duplicates can be allowed by setting the `DB_ALLOW_DUPS` flag on the database. - pub fn put(&self, key: &::Key, value: &D::Object) -> BldrResult<()> { + pub fn put(&self, key: &::Key, value: &D::Object) -> Result<()> { assert_txn_state_eq!(self.state(), TxnState::Normal); // JW TODO: these flags represent different types of "writes" and are dependent upon the // flags used to open the database. This would mean that this `put` function is the @@ -1275,7 +1245,7 @@ impl<'a, D: Database> RwTransaction<'a, D> { pub fn delete(&self, key: &::Key, value: Option<&D::Object>) - -> BldrResult<()> { + -> Result<()> { unsafe { let mut kval = key.to_mdb_value(); let dval: *mut lmdb_sys::MDB_val = { @@ -1292,7 +1262,7 @@ impl<'a, D: Database> RwTransaction<'a, D> { } impl<'a, D: 'a + Database> Transaction<'a, D> for RwTransaction<'a, D> { - fn begin(database: &'a D) -> BldrResult { + fn begin(database: &'a D) -> Result { let handle = try!(create_txn(database.env(), 0, None)); Ok(RwTransaction { database: database, @@ -1311,7 +1281,7 @@ impl<'a, D: 'a + Database> Transaction<'a, D> for RwTransaction<'a, D> { } } - fn commit(mut self) -> BldrResult<()> { + fn commit(mut self) -> Result<()> { assert_txn_state_eq!(self.state(), TxnState::Normal); unsafe { try_mdb!(lmdb_sys::mdb_txn_commit(self.handle())); @@ -1360,7 +1330,7 @@ impl PkgDatabase { impl Database for PkgDatabase { type Object = data_object::Package; - fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> BldrResult { + fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> Result { let env2 = env.clone(); let index = try!(PkgIndex::new().create(env2)); Ok(PkgDatabase { @@ -1374,7 +1344,7 @@ impl Database for PkgDatabase { PACKAGE_DB } - fn clear<'a, T: Transaction<'a, Self>>(&'a self, txn: &'a T) -> BldrResult<()> { + fn clear<'a, T: Transaction<'a, Self>>(&'a self, txn: &'a T) -> Result<()> { try!(txn.clear()); let nested = try!(txn.new_child_rw(&self.index)); try!(self.index.clear(&nested)); @@ -1382,7 +1352,7 @@ impl Database for PkgDatabase { Ok(()) } - fn write<'a>(&self, txn: &RwTransaction<'a, Self>, object: &Self::Object) -> BldrResult<()> { + fn write<'a>(&self, txn: &RwTransaction<'a, Self>, object: &Self::Object) -> Result<()> { try!(txn.put(object.ident(), object)); let nested = try!(txn.new_child_rw(&self.index)); try!(self.index.write(&nested, &object.ident)); @@ -1432,7 +1402,7 @@ impl PkgIndex { impl Database for PkgIndex { type Object = data_object::PackageIdent; - fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> BldrResult { + fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> Result { Ok(PkgIndex { env: env, handle: handle, @@ -1451,7 +1421,7 @@ impl Database for PkgIndex { self.handle } - fn write<'a>(&self, txn: &RwTransaction<'a, Self>, object: &Self::Object) -> BldrResult<()> { + fn write<'a>(&self, txn: &RwTransaction<'a, Self>, object: &Self::Object) -> Result<()> { try!(txn.put(&object.origin_idx(), object)); try!(txn.put(&object.name_idx(), object)); try!(txn.put(object.version_idx().as_ref().unwrap(), object)); @@ -1494,7 +1464,7 @@ impl ViewDatabase { } // Associate the given package to the given view - pub fn associate<'a, T: Database>(&self, txn: &RwTransaction<'a, T>, view: &::Object, pkg: &::Object) -> BldrResult<()> { + pub fn associate<'a, T: Database>(&self, txn: &RwTransaction<'a, T>, view: &::Object, pkg: &::Object) -> Result<()> { let nested = try!(txn.new_child_rw(&self.pkg_view_idx)); try!(nested.put(pkg.ident(), view)); let nested2 = try!(nested.new_child_rw(&self.view_pkg_idx)); @@ -1506,7 +1476,7 @@ impl ViewDatabase { impl Database for ViewDatabase { type Object = data_object::View; - fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> BldrResult { + fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> Result { let env2 = env.clone(); let env3 = env.clone(); let pkg_view_idx = try!(PkgViewIndex::new().create(env2)); @@ -1563,7 +1533,7 @@ impl PkgViewIndex { impl Database for PkgViewIndex { type Object = data_object::View; - fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> BldrResult { + fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> Result { Ok(PkgViewIndex { env: env, handle: handle, @@ -1620,7 +1590,7 @@ impl Database for ViewPkgIndex { VIEW_PACKAGE_INDEX } - fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> BldrResult { + fn open(env: Arc, handle: lmdb_sys::MDB_dbi) -> Result { Ok(ViewPkgIndex { env: env, handle: handle, @@ -1659,9 +1629,9 @@ impl Drop for ViewPkgIndex { mod tests { use std::path::Path; use super::*; - use super::super::data_object::*; - use error::{BldrError, ErrorKind}; - use package; + use error::Error; + use depot_core::data_object::*; + use bldr::package; // JW TODO: This test is ignored while I track down a bug preventing multiple transactions // being opened from different threads. @@ -1728,7 +1698,7 @@ mod tests { let txn = ds.views.txn_rw().unwrap(); txn.delete(&"my-view".to_string(), None).unwrap(); match txn.get(&"my-view".to_string()) { - Err(BldrError { err: ErrorKind::MdbError(MdbError::NotFound), .. }) => { + Err(Error::MdbError(MdbError::NotFound)) => { txn.abort(); assert!(true) } diff --git a/components/bldr/src/depot/doctor.rs b/components/depot/src/doctor.rs similarity index 96% rename from components/bldr/src/depot/doctor.rs rename to components/depot/src/doctor.rs index 2c554d5a22e..3e496fb12c4 100644 --- a/components/bldr/src/depot/doctor.rs +++ b/components/depot/src/doctor.rs @@ -9,14 +9,15 @@ use std::io; use std::path::PathBuf; use std::str::FromStr; +use bldr; +use bldr::package::{self, PackageArchive}; +use depot_core::data_object::{self, DataObject}; use time; use walkdir::WalkDir; use super::Depot; -use super::data_object::{self, DataObject}; use super::data_store::{Cursor, Database, Transaction}; -use error::{BldrError, BldrResult}; -use package::{self, PackageArchive}; +use error::Result; #[derive(Debug)] /// A struct containing the details of a repair run by `Doctor`. @@ -111,7 +112,7 @@ pub enum OperationType { #[derive(Debug)] pub enum Reason { BadArchive, - BadMetadata(BldrError), + BadMetadata(bldr::Error), BadPermissions, IO(io::Error), FileExists, @@ -143,7 +144,7 @@ impl<'a> Doctor<'a> { } } - fn run(mut self) -> BldrResult { + fn run(mut self) -> Result { try!(self.init_fs()); try!(self.truncate_database(&self.depot.datastore.packages)); try!(self.rebuild_metadata()); @@ -151,7 +152,7 @@ impl<'a> Doctor<'a> { Ok(self.report.generate()) } - fn init_fs(&mut self) -> BldrResult<()> { + fn init_fs(&mut self) -> Result<()> { match fs::metadata(&self.depot.path) { Ok(meta) => { if meta.is_file() { @@ -171,7 +172,7 @@ impl<'a> Doctor<'a> { Ok(()) } - fn rebuild_indices(&mut self) -> BldrResult<()> { + fn rebuild_indices(&mut self) -> Result<()> { let txn = try!(self.depot.datastore.views.pkg_view_idx.txn_rw()); { let tx2 = try!(txn.new_child_rw(&self.depot.datastore.views.view_pkg_idx)); @@ -213,7 +214,7 @@ impl<'a> Doctor<'a> { Ok(()) } - fn rebuild_metadata(&mut self) -> BldrResult<()> { + fn rebuild_metadata(&mut self) -> Result<()> { let mut directories = vec![]; for entry in WalkDir::new(&self.packages_path).follow_links(false) { let entry = entry.unwrap(); @@ -284,7 +285,7 @@ impl<'a> Doctor<'a> { Ok(()) } - fn truncate_database(&mut self, database: &D) -> BldrResult<()> { + fn truncate_database(&mut self, database: &D) -> Result<()> { let count = { let txn = try!(database.txn_rw()); let stats = try!(database.stat(&txn)); @@ -302,6 +303,6 @@ impl<'a> Doctor<'a> { /// /// Any files found within the metastore which are not valid or readable archives are moved into a /// gargbage directory for the user to examine. -pub fn repair(depot: &Depot) -> BldrResult { +pub fn repair(depot: &Depot) -> Result { Doctor::new(depot).run() } diff --git a/components/depot/src/error.rs b/components/depot/src/error.rs new file mode 100644 index 00000000000..1165250fd45 --- /dev/null +++ b/components/depot/src/error.rs @@ -0,0 +1,110 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::error; +use std::ffi; +use std::io; +use std::fmt; +use std::result; + +use bldr::{self, package}; +use hyper; + +use data_store; + +#[derive(Debug)] +pub enum Error { + BadPort(String), + BldrCore(bldr::Error), + DbInvalidPath, + HTTP(hyper::status::StatusCode), + InvalidPackageIdent(String), + IO(io::Error), + MdbError(data_store::MdbError), + NoXFilename, + NoFilePart, + NulError(ffi::NulError), + RemotePackageNotFound(package::PackageIdent), + WriteSyncFailed, +} + +pub type Result = result::Result; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match *self { + Error::BadPort(ref e) => format!("{} is an invalid port. Valid range 1-65535.", e), + Error::BldrCore(ref e) => format!("{}", e), + Error::DbInvalidPath => format!("Invalid filepath to internal datastore"), + Error::HTTP(ref e) => format!("{}", e), + Error::InvalidPackageIdent(ref e) => { + format!("Invalid package identifier: {:?}. A valid identifier is in the form \ + origin/name (example: chef/redis)", + e) + } + Error::IO(ref e) => format!("{}", e), + Error::MdbError(ref err) => format!("{}", err), + Error::NoXFilename => format!("Invalid download from a Depot - missing X-Filename header"), + Error::NoFilePart => { + format!("An invalid path was passed - we needed a filename, and this path does \ + not have one") + } + Error::NulError(ref e) => format!("{}", e), + Error::RemotePackageNotFound(ref pkg) => { + if pkg.fully_qualified() { + format!("Cannot find package in any sources: {}", pkg) + } else { + format!("Cannot find a release of package in any sources: {}", pkg) + } + } + Error::WriteSyncFailed => format!("Could not write to destination; perhaps the disk is full?"), + }; + write!(f, "{}", msg) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::BadPort(_) => "Received an invalid port or a number outside of the valid range.", + Error::BldrCore(ref err) => err.description(), + Error::DbInvalidPath => "A bad filepath was provided for an internal datastore", + Error::HTTP(_) => "Received an HTTP error", + Error::InvalidPackageIdent(_) => "Package identifiers must be in origin/name format (example: chef/redis)", + Error::IO(ref err) => err.description(), + Error::MdbError(_) => "Database error", + Error::NulError(_) => "An attempt was made to build a CString with a null byte inside it", + Error::RemotePackageNotFound(_) => "Cannot find a package in any sources", + Error::NoXFilename => "Invalid download from a Depot - missing X-Filename header", + Error::NoFilePart => "An invalid path was passed - we needed a filename, and this path does not have one", + Error::WriteSyncFailed => "Could not write to destination; bytes written was 0 on a non-0 buffer", + } + } +} + +impl From for Error { + fn from(err: bldr::Error) -> Error { + Error::BldrCore(err) + } +} + +impl From for Error { + fn from(err: data_store::MdbError) -> Error { + Error::MdbError(err) + } +} + +impl From for Error { + fn from(err: ffi::NulError) -> Error { + Error::NulError(err) + } +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IO(err) + } +} diff --git a/components/depot/src/lib.rs b/components/depot/src/lib.rs new file mode 100644 index 00000000000..7578c145ccb --- /dev/null +++ b/components/depot/src/lib.rs @@ -0,0 +1,120 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +extern crate bldr_core as bldr; +extern crate bldr_depot_core as depot_core; +extern crate bincode; +#[macro_use] +extern crate bitflags; +extern crate crypto; +#[macro_use] +extern crate hyper; +extern crate iron; +#[macro_use] +extern crate lazy_static; +extern crate libc; +#[macro_use] +extern crate log; +extern crate lmdb_sys; +#[macro_use] +extern crate router; +extern crate rustc_serialize; +extern crate time; +extern crate urlencoded; +extern crate walkdir; + +pub mod config; +pub mod error; +pub mod data_store; +pub mod doctor; +pub mod server; + +pub use self::config::Config; +pub use self::error::{Error, Result}; + +use std::net; +use std::sync::Arc; +use std::fs; +use std::path::{Path, PathBuf}; + +use crypto::sha2::Sha256; +use crypto::digest::Digest; + +use self::data_store::{DataStore, Database}; +use bldr::package::{self, PackageArchive}; + +pub struct Depot { + pub path: String, + pub datastore: DataStore, +} + +impl Depot { + pub fn new(path: String) -> Result> { + let dbpath = Path::new(&path).join("datastore"); + let datastore = try!(DataStore::open(dbpath.as_path())); + Ok(Arc::new(Depot { + path: path, + datastore: datastore, + })) + } + + // Return a PackageArchive representing the given package. None is returned if the Depot + // doesn't have an archive for the given package. + fn archive(&self, ident: &package::PackageIdent) -> Option { + let file = self.archive_path(&ident); + match fs::metadata(&file) { + Ok(_) => Some(PackageArchive::new(file)), + Err(_) => None, + } + } + + // Return a formatted string representing the filename of an archive for the given package + // identifier pieces. + fn archive_path>(&self, ident: T) -> PathBuf { + let ident = ident.as_ref(); + let mut digest = Sha256::new(); + let mut output = [0; 64]; + digest.input_str(&ident.to_string()); + digest.result(&mut output); + self.packages_path() + .join(format!("{:x}", output[0])) + .join(format!("{:x}", output[1])) + .join(format!("{}-{}-{}-{}.bldr", + &ident.origin, + &ident.name, + ident.version.as_ref().unwrap(), + ident.release.as_ref().unwrap())) + } + + fn key_path(&self, name: &str) -> PathBuf { + self.keys_path().join(format!("{}.asc", name)) + } + + fn keys_path(&self) -> PathBuf { + Path::new(&self.path).join("keys") + } + + fn packages_path(&self) -> PathBuf { + Path::new(&self.path).join("pkgs") + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ListenAddr(pub net::Ipv4Addr); +#[derive(Debug, PartialEq, Eq)] +pub struct ListenPort(pub u16); + +impl Default for ListenAddr { + fn default() -> Self { + ListenAddr(net::Ipv4Addr::new(0, 0, 0, 0)) + } +} + +impl Default for ListenPort { + fn default() -> Self { + ListenPort(9632) + } +} diff --git a/components/depot/src/main.rs b/components/depot/src/main.rs new file mode 100644 index 00000000000..ba5ff22b774 --- /dev/null +++ b/components/depot/src/main.rs @@ -0,0 +1,187 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +extern crate bldr_depot as depot; +extern crate bldr_depot_core as depot_core; +#[macro_use] +extern crate clap; +extern crate env_logger; +#[macro_use] +extern crate log; + +use std::process; +use std::str::FromStr; + +use depot::{server, Config, Error, Result}; +use depot::data_store::{self, Cursor, Database, Transaction}; +use depot_core::data_object; + +const DEFAULT_PATH: &'static str = "/opt/bldr/srvc/bldr/data"; +const VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +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!(BldrDepot => + (version: VERSION) + (about: "Manage a package Depot") + (@setting VersionlessSubcommands) + (@setting SubcommandRequiredElseHelp) + (@arg path: -p --path +takes_value "Filepath to service storage for the Depot service") + (@subcommand start => + (about: "Run a bldr package Depot") + (@arg port: --port +takes_value "Listen port. [default: 9632]") + ) + (@subcommand repair => + (about: "Verify and repair data integrity of the package Depot") + ) + (@subcommand repo => + (@subcommand create => + (about: "Create a new repository in the package Depot") + (@arg repo: +required "Name of the repository to create") + ) + (@subcommand list => + (about: "List repositories in the package Depot") + ) + ) + ) +} + +fn config_from_args(matches: &clap::ArgMatches) -> Result { + let cmd = matches.subcommand_name().unwrap(); + let args = matches.subcommand_matches(cmd).unwrap(); + let mut config = Config::new(); + if let Some(port) = args.value_of("port") { + if let Some(port) = u16::from_str(port).ok() { + config.port = depot::ListenPort(port); + } else { + return Err(Error::BadPort(port.to_string())); + } + } + config.path = args.value_of("path").unwrap_or(DEFAULT_PATH).to_string(); + Ok(config) +} + +fn dispatch(config: Config, matches: &clap::ArgMatches) -> Result<()> { + match matches.subcommand_name() { + Some("start") => start(&config), + Some("repair") => repair(&config), + Some(cmd @ "repo") => { + let args = matches.subcommand_matches(cmd).unwrap(); + match args.subcommand_name() { + Some(cmd @ "create") => { + let args = args.subcommand_matches(cmd).unwrap(); + let name = args.value_of("repo").unwrap(); + repo_create(name, &config) + } + Some("list") => repo_list(&config), + Some(cmd) => { + debug!("Dispatch failed, no match for command: {:?}", cmd); + Ok(()) + } + None => Ok(()), + } + } + Some(cmd) => { + debug!("Dispatch failed, no match for command: {:?}", cmd); + Ok(()) + } + None => Ok(()), + } +} + +/// Starts the depot server. +/// +/// # Failures +/// +/// * Fails if the depot server fails to start - canot bind to the port, etc. +fn start(config: &Config) -> Result<()> { + println!("Starting package Depot at {}", &config.path); + println!("Depot listening on {:?}", config.depot_addr()); + server::run(&config) +} + +/// Analyzes the integrity of the depot's metadata by comparing the metadata with the packages +/// on disk. If a package is found on disk that is not present in the metadata it is added to the +/// metadata and if an entry in the metadata doesn't have a matching package archive on disk the +/// entry is dropped from the database. +/// +/// # Failures +/// +/// * The database cannot be read +/// * A write transaction cannot be acquired +pub fn repair(config: &Config) -> Result<()> { + // JW TODO: should pass config to depot, not this path + let depot = try!(depot::Depot::new(config.path.clone())); + let report = try!(depot::doctor::repair(&depot)); + println!("Report: {:?}", &report); + Ok(()) +} + +/// Create a repository with the given name in the depot. +/// +/// # Failures +/// +/// * The database cannot be read +/// * A write transaction cannot be acquired. +fn repo_create(name: &str, config: &Config) -> Result<()> { + // JW TODO: should pass config to depot, not this path + let depot = try!(depot::Depot::new(config.path.clone())); + let txn = try!(depot.datastore.views.txn_rw()); + let object = data_object::View::new(name); + try!(depot.datastore.views.write(&txn, &object)); + Ok(()) +} + +/// List all repositories in the database. +/// +/// # Failures +/// +/// * The database cannot be read +/// * A read transaction cannot be acquired. +fn repo_list(config: &Config) -> Result<()> { + // JW TODO: should pass config to depot, not this path + let depot = try!(depot::Depot::new(config.path.clone())); + let mut views: Vec = vec![]; + let txn = try!(depot.datastore.views.txn_ro()); + let mut cursor = try!(txn.cursor_ro()); + match cursor.first() { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { + println!("No repositories. Create one with `bldr-depot repo create`."); + return Ok(()); + } + Err(e) => return Err(e), + Ok((_, value)) => views.push(value), + } + loop { + match cursor.next() { + Ok((_, value)) => views.push(value), + Err(_) => break, + } + } + println!("Listing {} repositories", views.len()); + for view in views.iter() { + println!(" {}", view); + } + Ok(()) +} + +fn exit_with(err: Error, code: i32) { + println!("{:?}", err); + process::exit(code) +} diff --git a/components/bldr/src/depot/mod.rs b/components/depot/src/server.rs similarity index 76% rename from components/bldr/src/depot/mod.rs rename to components/depot/src/server.rs index 29065fe3914..70d3afca6bc 100644 --- a/components/bldr/src/depot/mod.rs +++ b/components/depot/src/server.rs @@ -4,13 +4,12 @@ // this file ("Licensee") apply to Licensee's use of the Software until such time that the Software // is made available under an open source license such as the Apache 2.0 License. -pub mod client; -pub mod data_object; -pub mod data_store; -pub mod doctor; +use std::fs::{self, File}; +use std::io::{Read, Write, BufWriter}; +use std::path::{Path, PathBuf}; -use crypto::sha2::Sha256; -use crypto::digest::Digest; +use depot_core::{ETag, XFileName}; +use depot_core::data_object::{self, DataObject}; use iron::prelude::*; use iron::status; use iron::request::Body; @@ -19,113 +18,13 @@ use router::{Params, Router}; use rustc_serialize::json; use urlencoded::UrlEncodedQuery; -use std::net; -use std::sync::Arc; -use std::fs::{self, File}; -use std::io::{Read, Write, BufWriter}; -use std::path::{Path, PathBuf}; - -use error::{BldrError, BldrResult, ErrorKind}; +use super::Depot; use config::Config; -use self::data_store::{Cursor, DataStore, Database, Transaction}; -use self::data_object::DataObject; -use package::{self, PackageArchive}; - -static LOGKEY: &'static str = "RE"; - -header! { (XFileName, "X-Filename") => [String] } -header! { (ETag, "ETag") => [String] } - -pub struct Depot { - pub path: String, - pub datastore: DataStore, -} - -impl Depot { - pub fn new(path: String) -> BldrResult> { - let dbpath = Path::new(&path).join("datastore"); - let datastore = try!(DataStore::open(dbpath.as_path())); - Ok(Arc::new(Depot { - path: path, - datastore: datastore, - })) - } - - // Return a PackageArchive representing the given package. None is returned if the Depot - // doesn't have an archive for the given package. - fn archive(&self, ident: &package::PackageIdent) -> Option { - let file = self.archive_path(&ident); - match fs::metadata(&file) { - Ok(_) => Some(PackageArchive::new(file)), - Err(_) => None, - } - } - - // Return a formatted string representing the filename of an archive for the given package - // identifier pieces. - fn archive_path>(&self, ident: T) -> PathBuf { - let ident = ident.as_ref(); - let mut digest = Sha256::new(); - let mut output = [0; 64]; - digest.input_str(&ident.to_string()); - digest.result(&mut output); - self.packages_path() - .join(format!("{:x}", output[0])) - .join(format!("{:x}", output[1])) - .join(format!("{}-{}-{}-{}.bldr", - &ident.origin, - &ident.name, - ident.version.as_ref().unwrap(), - ident.release.as_ref().unwrap())) - } - - fn key_path(&self, name: &str) -> PathBuf { - self.keys_path().join(format!("{}.asc", name)) - } - - fn keys_path(&self) -> PathBuf { - Path::new(&self.path).join("keys") - } - - fn packages_path(&self) -> PathBuf { - Path::new(&self.path).join("pkgs") - } -} - -#[derive(Debug, PartialEq, Eq)] -pub struct ListenAddr(pub net::Ipv4Addr); -#[derive(Debug, PartialEq, Eq)] -pub struct ListenPort(pub u16); - -impl Default for ListenAddr { - fn default() -> Self { - ListenAddr(net::Ipv4Addr::new(0, 0, 0, 0)) - } -} - -impl Default for ListenPort { - fn default() -> Self { - ListenPort(9632) - } -} - -impl<'a> Into for &'a Params { - fn into(self) -> package::PackageIdent { - package::PackageIdent::new(self.find("origin").unwrap(), - self.find("pkg").unwrap(), - self.find("version"), - self.find("release")) - } -} +use data_store::{self, Cursor, Database, Transaction}; +use error::{Error, Result}; +use bldr::package::{self, PackageArchive}; -impl<'a> Into for &'a Params { - fn into(self) -> data_object::PackageIdent { - let ident: package::PackageIdent = self.into(); - data_object::PackageIdent::new(ident) - } -} - -fn write_file(filename: &PathBuf, body: &mut Body) -> BldrResult { +fn write_file(filename: &PathBuf, body: &mut Body) -> Result { let path = filename.parent().unwrap(); try!(fs::create_dir_all(path)); let tempfile = format!("{}.tmp", filename.to_string_lossy()); @@ -144,19 +43,19 @@ fn write_file(filename: &PathBuf, body: &mut Body) -> BldrResult { // Write the buffer to the BufWriter on the Heap let bytes_written = try!(writer.write(&buf[0..len])); if bytes_written == 0 { - return Err(bldr_error!(ErrorKind::WriteSyncFailed)); + return Err(Error::WriteSyncFailed); } written = written + (bytes_written as i64); } }; } - outputln!("File added to Depot at {}", filename.to_string_lossy()); + info!("File added to Depot at {}", filename.to_string_lossy()); try!(fs::rename(&tempfile, &filename)); Ok(true) } fn upload_key(depot: &Depot, req: &mut Request) -> IronResult { - outputln!("Upload Key {:?}", req); + info!("Upload Key {:?}", req); let rext = req.extensions.get::().unwrap(); let key = rext.find("key").unwrap(); let file = depot.key_path(&key); @@ -173,13 +72,13 @@ fn upload_key(depot: &Depot, req: &mut Request) -> IronResult { } fn upload_package(depot: &Depot, req: &mut Request) -> IronResult { - outputln!("Upload {:?}", req); + info!("Upload {:?}", req); let checksum = match extract_query_value("checksum", req) { Some(checksum) => checksum, None => return Ok(Response::with(status::BadRequest)), }; let params = req.extensions.get::().unwrap(); - let ident: package::PackageIdent = params.into(); + let ident: package::PackageIdent = extract_ident(params); if !ident.fully_qualified() { return Ok(Response::with(status::BadRequest)); @@ -235,7 +134,7 @@ fn upload_package(depot: &Depot, req: &mut Request) -> IronResult { } fn download_key(depot: &Depot, req: &mut Request) -> IronResult { - outputln!("Download {:?}", req); + info!("Download {:?}", req); let rext = req.extensions.get::().unwrap(); let key = match rext.find("key") { @@ -254,9 +153,9 @@ fn download_key(depot: &Depot, req: &mut Request) -> IronResult { } fn download_package(depot: &Depot, req: &mut Request) -> IronResult { - outputln!("Download {:?}", req); + info!("Download {:?}", req); let params = req.extensions.get::().unwrap(); - let ident: data_object::PackageIdent = params.into(); + let ident: data_object::PackageIdent = extract_data_ident(params); let result = { let txn = try!(depot.datastore.packages.txn_ro()); @@ -288,7 +187,7 @@ fn download_package(depot: &Depot, req: &mut Request) -> IronResult { panic!("Inconsistent package metadata! Exit and run `bldr-depot repair` to fix data integrity."); } } - Err(BldrError { err: ErrorKind::MdbError(data_store::MdbError::NotFound), ..}) => { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { Ok(Response::with((status::NotFound))) } Err(_) => unreachable!("unknown error"), @@ -301,7 +200,7 @@ fn list_packages(depot: &Depot, req: &mut Request) -> IronResult { if let Some(view) = params.find("repo") { list_packages_scoped_to_repo(depot, view) } else { - let ident: data_object::PackageIdent = params.into(); + let ident: data_object::PackageIdent = extract_data_ident(params); let mut packages: Vec = vec![]; let txn = try!(depot.datastore.packages.index.txn_ro()); @@ -317,7 +216,7 @@ fn list_packages(depot: &Depot, req: &mut Request) -> IronResult { } Ok(()) } - Err(e) => Err(BldrError::from(e)), + Err(e) => Err(Error::from(e)), }; match result { @@ -325,7 +224,7 @@ fn list_packages(depot: &Depot, req: &mut Request) -> IronResult { let body = json::encode(&packages).unwrap(); Ok(Response::with((status::Ok, body))) } - Err(BldrError { err: ErrorKind::MdbError(data_store::MdbError::NotFound), ..}) => { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { Ok(Response::with((status::NotFound))) } Err(_) => unreachable!("unknown error"), @@ -350,7 +249,7 @@ fn list_packages_scoped_to_repo(depot: &Depot, view: &str) -> IronResult { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { Ok(Response::with((status::NotFound))) } Err(_) => unreachable!("unknown error"), @@ -373,7 +272,7 @@ fn list_repos(depot: &Depot, _req: &mut Request) -> IronResult { fn show_package(depot: &Depot, req: &mut Request) -> IronResult { let params = req.extensions.get::().unwrap(); - let ident: data_object::PackageIdent = params.into(); + let ident: data_object::PackageIdent = extract_data_ident(params); if let Some(repo) = params.find("repo") { if let Some(pkg) = try!(latest_package_in_repo(&ident, depot, repo)) { @@ -413,7 +312,7 @@ fn show_package(depot: &Depot, req: &mut Request) -> IronResult { response.headers.set(ETag(data.checksum)); Ok(response) } - Err(BldrError { err: ErrorKind::MdbError(data_store::MdbError::NotFound), ..}) => { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { Ok(Response::with((status::NotFound))) } Err(e) => unreachable!("unknown error: {:?}", e), @@ -425,7 +324,7 @@ fn latest_package_in_repo> (ident: P, depot: &Depot, repo: &str) - -> BldrResult> { + -> Result> { let txn = try!(depot.datastore.views.view_pkg_idx.txn_ro()); let mut cursor = try!(txn.cursor_ro()); match cursor.set_key(&repo.to_string()) { @@ -440,7 +339,7 @@ fn latest_package_in_repo> pkg = next; continue; } - Err(BldrError { err: ErrorKind::MdbError(data_store::MdbError::NotFound), ..}) => { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { return Ok(None); } Err(_) => unreachable!("unknown error"), @@ -448,7 +347,7 @@ fn latest_package_in_repo> } } } - Err(BldrError { err: ErrorKind::MdbError(data_store::MdbError::NotFound), ..}) => { + Err(Error::MdbError(data_store::MdbError::NotFound)) => { return Ok(None); } Err(_) => unreachable!("unknown error"), @@ -462,7 +361,7 @@ fn promote_package(depot: &Depot, req: &mut Request) -> IronResult { let txn = try!(depot.datastore.views.txn_rw()); match txn.get(&repo.to_string()) { Ok(view) => { - let ident: package::PackageIdent = params.into(); + let ident: package::PackageIdent = extract_ident(params); let nested = try!(txn.new_child_rw(&depot.datastore.packages)); match nested.get(&ident.to_string()) { Ok(package) => { @@ -477,6 +376,18 @@ fn promote_package(depot: &Depot, req: &mut Request) -> IronResult { } } +fn extract_ident(params: &Params) -> package::PackageIdent { + package::PackageIdent::new(params.find("origin").unwrap(), + params.find("pkg").unwrap(), + params.find("version"), + params.find("release")) +} + +fn extract_data_ident(params: &Params) -> data_object::PackageIdent { + let ident: package::PackageIdent = extract_ident(params); + data_object::PackageIdent::new(ident) +} + fn extract_query_value(key: &str, req: &mut Request) -> Option { match req.get_ref::() { Ok(map) => { @@ -494,8 +405,8 @@ fn extract_query_value(key: &str, req: &mut Request) -> Option { } } -pub fn run(config: &Config) -> BldrResult<()> { - let depot = try!(Depot::new(String::from(config.path()))); +pub fn run(config: &Config) -> Result<()> { + let depot = try!(Depot::new(config.path.clone())); let depot1 = depot.clone(); let depot2 = depot.clone(); let depot3 = depot.clone(); @@ -540,3 +451,12 @@ pub fn run(config: &Config) -> BldrResult<()> { Iron::new(router).http(config.depot_addr()).unwrap(); Ok(()) } + +impl From for IronError { + fn from(err: Error) -> IronError { + IronError { + error: Box::new(err), + response: Response::with((status::InternalServerError, "Internal bldr error")), + } + } +} diff --git a/components/depot/tests/server.rs b/components/depot/tests/server.rs new file mode 100644 index 00000000000..1cb21599349 --- /dev/null +++ b/components/depot/tests/server.rs @@ -0,0 +1,142 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +extern crate regex; +extern crate time; +extern crate hyper; +extern crate url; +extern crate bldr_core as core; +extern crate uuid; +extern crate rustc_serialize; + +macro_rules! poerr { + ($expr:expr) => ( + match $expr { + Ok(val) => val, + Err(e) => { + panic!("{:?}", e) + } + } + ) +} + +macro_rules! poerr_ref { + ($expr:expr) => ( + match $expr { + Ok(ref val) => val, + Err(ref e) => { + panic!("{:?}", e) + } + } + ) +} + +/// Given a Cmd struct and a list of status codes, fails +/// if the command didn't exit with one of the status codes. +macro_rules! assert_cmd_exit_code { + ($cmd:ident, [ $( $status:expr ),+ ]) => { + match $cmd.status().code() { + Some(value) => { + let codes = [$($status),+]; + assert!(codes.into_iter().any(|x| *x == value), "Status code {} does not match {:?}", value, codes) + }, + None => { + panic!("Command has not finished - cannot assert exit code") + } + } + } +} + +/// Given a string and a regex (use the r".." syntax), assert that +/// the string matches the regex. +macro_rules! assert_regex { + ($string:expr, $regexp:expr) => { + { + use regex::Regex; + + let re = Regex::new($regexp).unwrap(); + assert!(re.is_match($string), "Regex '{}' failed to match", $regexp); + } + } +} + +macro_rules! assert_docker_log { + ($docker:expr, $regexp:expr) => { + { + assert!($docker.wait_until($regexp), "Regex '{}' failed to match", $regexp); + } + } +} + +macro_rules! assert_docker_log_count { + ($count:expr, $regexp:expr, [ $( $docker:expr ),+ ]) => { + { + let responses = [ $( $docker.wait_until($regexp) ),+ ]; + let num_responses = responses.iter().fold(0, |acc, &item| { let x = if item == true { 1 } else { 0 }; acc + x }); + assert!(num_responses == $count, "Expected {} occurances of {}; got {}", $count, $regexp, num_responses); + } + } +} + +macro_rules! assert_file_exists { + ($string:expr) => { + { + use std::fs; + + let meta = match fs::metadata($string) { + Ok(meta) => meta, + Err(e) => panic!("{} does not exist - {:?}", $string, e) + }; + assert!(meta.is_file(), "{} exists, but is not a file", $string) + } + } +} + +macro_rules! assert_file_exists_in_studio { + ($string:expr) => { + { + use std::fs; + let path = format!("/opt/studios/functional-tests{}", $string); + let meta = match fs::metadata(&path) { + Ok(meta) => meta, + Err(e) => panic!("{} does not exist - {:?}", path, e) + }; + assert!(meta.is_file(), "{} exists, but is not a file", path) + } + } +} + +mod support; + +use support::{command, docker, setup}; + +#[test] +#[ignore] +fn upload_a_package_and_then_install_it() { + setup::gpg_import(); + setup::key_install(); + setup::simple_service(); + let d = docker::depot("test/simple_service"); + let ipaddress = d.ipaddress(); + + let mut upload = command::bldr(&["upload", + "test/simple_service", + "-u", + &format!("http://{}:9632", ipaddress)]) + .unwrap(); + upload.wait_with_output(); + assert_cmd_exit_code!(upload, [0]); + assert_regex!(upload.stdout(), r"Upload Bldr Package (.+)"); + assert_regex!(upload.stdout(), r"Uploading from (.+)"); + assert_regex!(upload.stdout(), r"Complete"); + let mut install = command::bldr(&["install", + "test/simple_service", + "-u", + &format!("http://{}:9632", ipaddress)]) + .unwrap(); + install.wait_with_output(); + assert_cmd_exit_code!(install, [0]); +} diff --git a/components/depot/tests/support/command.rs b/components/depot/tests/support/command.rs new file mode 100644 index 00000000000..8c6ddaa1217 --- /dev/null +++ b/components/depot/tests/support/command.rs @@ -0,0 +1,201 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::io::prelude::*; +use std::io; +use std::process::{Command, Child, Stdio, ExitStatus}; +use std::fmt; +use std::error::Error; +use std::result; +use std::thread; +use std::collections::HashMap; + +pub struct Cmd { + pub child: Option, + pub status: Option, + pub stdout: Option, + pub stderr: Option, +} + +impl Cmd { + pub fn stdout(&self) -> &str { + match self.stdout { + Some(ref stdout) => stdout, + None => panic!("No stdout available - process needs a wait"), + } + } + + pub fn stderr(&self) -> &str { + match self.stderr { + Some(ref stderr) => stderr, + None => panic!("No stderr available - process needs a wait"), + } + } + + pub fn status(&self) -> &ExitStatus { + match self.status { + Some(ref status) => status, + None => panic!("No status available - process needs a wait or kill"), + } + } + + pub fn wait_with_output(&mut self) -> &Self { + // The child is unavailable for more calls after this + let child = self.child.take().unwrap(); + + let output = match child.wait_with_output() { + Ok(output) => output, + Err(e) => panic!("{:?}", e), + }; + self.status = Some(output.status); + let stdout = String::from_utf8(output.stdout).unwrap_or_else(|x| panic!("{:?}", x)); + let stderr = String::from_utf8(output.stderr).unwrap_or_else(|x| panic!("{:?}", x)); + println!("OUT: {}", stdout); + println!("ERR: {}", stderr); + self.stdout = Some(stdout); + self.stderr = Some(stderr); + self + } +} + +#[derive(Debug)] +pub enum CmdError { + Io(io::Error), +} + +pub type CmdResult = result::Result; + +impl fmt::Display for CmdError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CmdError::Io(ref err) => err.fmt(f), + } + } +} + +impl Error for CmdError { + fn description(&self) -> &str { + match *self { + CmdError::Io(ref err) => err.description(), + } + } +} + +impl From for CmdError { + fn from(err: io::Error) -> CmdError { + CmdError::Io(err) + } +} + +#[derive(Debug)] +pub struct CommandArgs { + pub cmd: String, + pub args: Vec, + pub env: HashMap, + pub cwd: Option, +} + +impl CommandArgs { + fn new>(cmd: S) -> CommandArgs { + CommandArgs { + cmd: cmd.into(), + args: Vec::new(), + env: HashMap::new(), + cwd: None, + } + } + + fn arg>(&mut self, arg: S) -> &mut CommandArgs { + self.args.push(arg.into()); + self + } + + fn env>(&mut self, k: S, v: S) -> &mut CommandArgs { + self.env.insert(k.into(), v.into()); + self + } +} + +impl fmt::Display for CommandArgs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "Command: C: {} A: {:?} E: {:?} CWD: {:?}", + self.cmd, + self.args, + self.env, + self.cwd) + } +} + + +pub fn command(cmd: &str, args: &[&str]) -> Command { + command_with_env(cmd, args, None) +} + +pub fn command_with_env(cmd: &str, args: &[&str], env: Option<&HashMap<&str, &str>>) -> Command { + let mut cmd_args = CommandArgs::new(cmd); + for a in args { + cmd_args.arg(*a); + } + if let Some(real_env) = env { + for (k, v) in real_env { + cmd_args.env(*k, *v); + } + } + run_command(cmd_args) +} + +pub fn run_command(cmd_args: CommandArgs) -> Command { + println!("{}: {}", + thread::current().name().unwrap_or("main"), + cmd_args); + + let mut command = Command::new(&cmd_args.cmd); + command.args(&cmd_args.args); + command.stdin(Stdio::null()); + command.stdout(Stdio::piped()); + command.stderr(Stdio::piped()); + + for (k, v) in cmd_args.env { + command.env(k, v); + } + + command +} + +pub fn spawn(mut command: Command) -> CmdResult { + let child = try!(command.spawn()); + Ok(Cmd { + child: Some(child), + status: None, + stdout: None, + stderr: None, + }) +} + +pub fn studio_run(cmd: &str, args: &[&str]) -> CmdResult { + let real_cmd = "studio"; + let mut real_args = vec!["-r", "/opt/studios/functional-tests", "run", cmd]; + real_args.extend_from_slice(args); + let mut command = command(real_cmd, &real_args[..]); + command.current_dir("/src"); + spawn(command) +} + +pub fn run(cmd: &str, args: &[&str]) -> CmdResult { + let command = command(cmd, args); + spawn(command) +} + +pub fn bldr_build(to_build: &str) -> CmdResult { + studio_run("/src/plans/bldr-build", &[to_build]) +} + +pub fn bldr(args: &[&str]) -> CmdResult { + let bldr = super::path::bldr(); + let command = command(&bldr, args); + spawn(command) +} diff --git a/components/depot/tests/support/docker.rs b/components/depot/tests/support/docker.rs new file mode 100644 index 00000000000..53fb47e3201 --- /dev/null +++ b/components/depot/tests/support/docker.rs @@ -0,0 +1,75 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +//! Start a docker container, store the instance id +//! Get the logs +//! Stop the container +//! Get the logs +//! Remove the container on drop + +use super::command; +use std::thread; +use std::env; + +#[derive(Debug)] +pub struct Docker { + pub container_id: String, +} + +fn docker_cmd(args: &[&str]) -> Docker { + println!("{}: Starting docker with {:?}", + thread::current().name().unwrap_or("main"), + args); + let mut cmd = command::run("docker", args).unwrap_or_else(|x| panic!("{:?}", x)); + cmd.wait_with_output(); + let mut id = String::from(cmd.stdout()); + id.pop(); + println!("{}: Docker exited with: {:?}, stdout: {}, stderr: {}", + thread::current().name().unwrap_or("main"), + cmd.status().code(), + cmd.stdout(), + cmd.stderr()); + println!("{}: Docker container: {}", + id, + thread::current().name().unwrap_or("main")); + Docker { container_id: String::from(id) } +} + +pub fn depot(image: &str) -> Docker { + docker_cmd(&["run", "-d", "--cap-add=NET_ADMIN", "--expose=9632", image, "depot"]) +} + +impl Docker { + pub fn ipaddress(&self) -> String { + let mut cmd = command::run("sh", + &["-c", + &format!("docker inspect --format='{}' {}", + "{{range .NetworkSettings.Networks}}{{.\ + IPAddress}}{{end}}", + &self.container_id)]) + .unwrap_or_else(|x| panic!("{:?}", x)); + cmd.wait_with_output(); + let ipaddress = String::from(cmd.stdout().trim()); + println!("I have ipaddress {}", &ipaddress); + ipaddress + } +} + +impl Drop for Docker { + fn drop(&mut self) { + if thread::panicking() { + if let None = env::var_os("BLDR_DOCKER_KEEP") { + let mut cmd = command::run("docker", &["rm", "-f", &self.container_id]) + .unwrap_or_else(|x| panic!("{:?}", x)); + cmd.wait_with_output(); + } + } else { + let mut cmd = command::run("docker", &["rm", "-f", &self.container_id]) + .unwrap_or_else(|x| panic!("{:?}", x)); + cmd.wait_with_output(); + } + } +} diff --git a/components/depot/tests/support/mod.rs b/components/depot/tests/support/mod.rs new file mode 100644 index 00000000000..150b62117ef --- /dev/null +++ b/components/depot/tests/support/mod.rs @@ -0,0 +1,10 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +pub mod setup; +pub mod path; +pub mod command; +pub mod docker; diff --git a/components/depot/tests/support/path.rs b/components/depot/tests/support/path.rs new file mode 100644 index 00000000000..992c2859d2f --- /dev/null +++ b/components/depot/tests/support/path.rs @@ -0,0 +1,29 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::env; +use std::path::PathBuf; + +pub fn exe_path() -> PathBuf { + env::current_exe().unwrap() +} + +pub fn root() -> PathBuf { + exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests") +} + +pub fn fixtures() -> PathBuf { + root().join("fixtures") +} + +pub fn fixture_as_string(name: &str) -> String { + let fixture_string = fixtures().join(name).to_string_lossy().into_owned(); + fixture_string +} + +pub fn bldr() -> String { + root().parent().unwrap().join("target/debug/bldr").to_string_lossy().into_owned() +} diff --git a/components/depot/tests/support/setup.rs b/components/depot/tests/support/setup.rs new file mode 100644 index 00000000000..bd9431e25e8 --- /dev/null +++ b/components/depot/tests/support/setup.rs @@ -0,0 +1,67 @@ +// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc. +// +// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing +// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software +// is made available under an open source license such as the Apache 2.0 License. + +use std::sync::{Once, ONCE_INIT}; + +pub fn gpg_import() { + static ONCE: Once = ONCE_INIT; + ONCE.call_once(|| { + let mut gpg = match super::command::studio_run("gpg", + &["--import", + &super::path::fixture_as_string("chef-private.gpg")]) { + Ok(cmd) => cmd, + Err(e) => panic!("{:?}", e), + }; + gpg.wait_with_output(); + if !gpg.status.unwrap().success() { + match gpg.stderr { + Some(stderr) => { + use regex::Regex; + let re = Regex::new("already in secret keyring").unwrap(); + if !re.is_match(&stderr) { + panic!("Failed to import gpg keys"); + } + } + None => panic!("Failed to import gpg keys") + } + } + }); +} + +pub fn simple_service() { + static ONCE: Once = ONCE_INIT; + ONCE.call_once(|| { + let mut simple_service = + match super::command::bldr_build(&super::path::fixture_as_string("simple_service")) { + Ok(cmd) => cmd, + Err(e) => panic!("{:?}", e), + }; + simple_service.wait_with_output(); + if !simple_service.status.unwrap().success() { + panic!("Failed to build simple service"); + } + let mut docker = match super::command::studio_run("dockerize", &["test/simple_service"]) { + Ok(cmd) => cmd, + Err(e) => panic!("{:?}", e), + }; + docker.wait_with_output(); + if !docker.status.unwrap().success() { + panic!("Failed to dockerize simple service"); + } + }); +} + +pub fn key_install() { + static ONCE: Once = ONCE_INIT; + ONCE.call_once(|| { + let mut cmd = match super::command::bldr(&["key", + &super::path::fixture_as_string("chef-public.asc")]) { + Ok(cmd) => cmd, + Err(e) => panic!("{:?}", e), + }; + cmd.wait_with_output(); + }); +}