diff --git a/Cargo.toml b/Cargo.toml index 1bd5f041..75b5432e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,23 @@ homepage = "http://cage.faraday.io/" build = "build.rs" [features] -default = ["serde_codegen", "compose_yml/default"] -unstable = ["serde_derive", "compose_yml/unstable", "clippy"] +# This build of `cage` is our standard, official build. +default = ["default_minimal", "openssl"] + +# This build of `cage` relies on nightly Rust features. +unstable = ["unstable_minimal", "openssl", "clippy"] + +# OpenSSL causes build problems on less popular platforms, especially for +# our binary builds. And we only need it for certain advanced features. +# So we allow it to be disabled using: +# +# cargo build --no-default-features --features default_minimal +openssl = ["hashicorp_vault"] + +# You must always enable one of these features or the other to get serde to +# build. +default_minimal = ["serde_codegen", "compose_yml/default"] +unstable_minimal = ["serde_derive", "compose_yml/unstable"] [[bin]] name = "cage" @@ -35,7 +50,7 @@ env_logger = "0.3.4" error-chain = "0.5.0" glob = "0.2.11" handlebars = "0.21.0" -hashicorp_vault = "0.6.1" +hashicorp_vault = { version = "0.6.1", optional = true } includedir = "0.2.1" lazy_static = "0.2.1" log = "0.3.6" diff --git a/src/errors.rs b/src/errors.rs index 0c362611..69cebf56 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -15,7 +15,6 @@ use std::ffi::OsString; use std::io; use std::path::{PathBuf, StripPrefixError}; use std::string::FromUtf8Error; -use vault; use project::PROJECT_CONFIG_PATH; use version; @@ -35,7 +34,6 @@ error_chain! { glob::PatternError, GlobPattern; io::Error, Io; StripPrefixError, StripPrefix; - vault::Error, Vault; } errors { @@ -63,6 +61,13 @@ error_chain! { display("could not write to '{}'", path.display()) } + /// A feature was disabled at compile time. + FeatureDisabled { + description("feature disabled at compile time") + display("this feature was disabled when the application was \ + compiled (you may want to rebuild from source)") + } + /// This project specified that it required a different version of /// this tool. MismatchedVersion(required: semver::VersionReq) { @@ -110,6 +115,12 @@ error_chain! { source ls`)", &source_alias) } + + /// We were unable to communicate with the specified Vault server. + VaultError(url: String) { + description("an error occurred talking to a Vault server") + display("an error occurred talking to the Vault server at {}", &url) + } } } diff --git a/src/lib.rs b/src/lib.rs index 76dd531b..c95583fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ extern crate env_logger; extern crate error_chain; extern crate glob; extern crate handlebars; +#[cfg(feature="hashicorp_vault")] extern crate hashicorp_vault as vault; extern crate includedir; #[macro_use] diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index e51fb18e..d7cf9476 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -123,13 +123,13 @@ impl Manager { // We instantiate some of these plugins twice, could we be more // clever about it? try!(manager.register_generator::(proj)); - try!(manager.register_generator::(proj)); + try!(manager.register_vault_generator(proj)); try!(manager.register_transform::(proj)); try!(manager.register_transform::(proj)); try!(manager.register_transform::(proj)); try!(manager.register_transform::(proj)); - try!(manager.register_transform::(proj)); + try!(manager.register_vault_transform(proj)); // Run this last, in case it wants to remove any labels used by // other plugins. @@ -160,6 +160,21 @@ impl Manager { Ok(()) } + /// Register our vault generator. We put this in a separate function + /// so we can use `cfg`. + #[cfg(feature="hashicorp_vault")] + fn register_vault_generator(&mut self, proj: &Project) -> Result<()> { + self.register_generator::(proj) + } + + /// Pretend to register our vault generator, but just leave a note in + /// the logs. + #[cfg(not(feature="hashicorp_vault"))] + fn register_vault_generator(&mut self, _: &Project) -> Result<()> { + debug!("vault generator was disabled at build time"); + Ok(()) + } + /// Register a transform with this manager. fn register_transform(&mut self, proj: &Project) -> Result<()> where T: PluginNew + PluginTransform + 'static @@ -171,6 +186,30 @@ impl Manager { Ok(()) } + /// Register our vault transform. We put this in a separate function + /// so we can use `cfg`. + #[cfg(feature="hashicorp_vault")] + fn register_vault_transform(&mut self, proj: &Project) -> Result<()> { + self.register_transform::(proj) + } + + /// Pretend to register our vault transform, but just leave a note in + /// the logs. + #[cfg(not(feature="hashicorp_vault"))] + fn register_vault_transform(&mut self, _: &Project) -> Result<()> { + debug!("vault transform was disabled at build time"); + Ok(()) + } + + /// A plugin was missing, so build an appropriate error message. + fn missing_plugin(&self, name: &str) -> ErrorKind { + if name == "vault" { + ErrorKind::FeatureDisabled + } else { + unreachable!("Cannot find a generator named {}", name) + } + } + /// Run the specified generator in the current project. pub fn generate(&self, project: &Project, @@ -180,7 +219,7 @@ impl Manager { let generator = try!(self.generators .iter() .find(|g| g.name() == name) - .ok_or_else(|| err!("Cannot find a generator named {}", name))); + .ok_or_else(|| self.missing_plugin(name))); debug!("Generating {}", generator.name()); generator.generate(project, out) } @@ -192,6 +231,7 @@ impl Manager { file: &mut dc::File) -> Result<()> { for plugin in &self.transforms { + trace!("transforming '{}' with {}", ctx.pod.name(), plugin.name()); try!(plugin.transform(op, ctx, file) .chain_err(|| ErrorKind::PluginFailed(plugin.name().to_owned()))); } diff --git a/src/plugins/transform/mod.rs b/src/plugins/transform/mod.rs index 25bcbf29..2904c736 100644 --- a/src/plugins/transform/mod.rs +++ b/src/plugins/transform/mod.rs @@ -5,4 +5,5 @@ pub mod default_tags; pub mod labels; pub mod secrets; pub mod sources; +#[cfg(feature="hashicorp_vault")] pub mod vault; diff --git a/src/plugins/transform/vault.rs b/src/plugins/transform/vault.rs index 38e4c606..a70d4c23 100644 --- a/src/plugins/transform/vault.rs +++ b/src/plugins/transform/vault.rs @@ -175,18 +175,21 @@ impl GenerateToken for Vault { policies: Vec, ttl: VaultDuration) -> Result { + let mkerr = || ErrorKind::VaultError(self.addr.clone()); + // We can't store `client` in `self`, because it has some obnoxious // lifetime parameters. So we'll just recreate it. This is // probably not the worst idea, because it uses `hyper` for HTTP, // and `hyper` HTTP connections used to have expiration issues that // were tricky for clients to deal with correctly. - let client = try!(vault::Client::new(&self.addr, &self.token)); + let client = try!(vault::Client::new(&self.addr, &self.token) + .chain_err(&mkerr)); let opts = vault::client::TokenOptions::default() .display_name(display_name) .renewable(true) .ttl(ttl) .policies(policies); - let auth = try!(client.create_token(&opts)); + let auth = try!(client.create_token(&opts).chain_err(&mkerr)); Ok(auth.client_token) } }