Skip to content

Commit

Permalink
Add event stream token to CLI and plumb through
Browse files Browse the repository at this point in the history
Signed-off-by: Christopher Maier <cmaier@chef.io>
  • Loading branch information
christophermaier authored and Gina Peers committed Apr 17, 2019
1 parent 8aa96fc commit 74f255a
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 16 deletions.
5 changes: 5 additions & 0 deletions components/common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub enum Error {
IO(io::Error),
/// Errors when joining paths :)
JoinPathsError(env::JoinPathsError),
MissingCLIInputError(String),
NetParseError(net::AddrParseError),
OfflineArtifactNotFound(PackageIdent),
OfflineOriginKeyNotFound(String),
Expand Down Expand Up @@ -119,6 +120,9 @@ impl fmt::Display for Error {
s)
}
Error::HabitatCore(ref e) => format!("{}", e),
Error::MissingCLIInputError(ref arg) => {
format!("Missing required CLI argument!: {}", arg)
}
Error::InstallHookFailed(ref ident) => {
format!("Install hook exited unsuccessfully: {}", ident)
}
Expand Down Expand Up @@ -193,6 +197,7 @@ impl error::Error for Error {
Error::InvalidInstallHookMode(_) => "Invalid InstallHookMode",
Error::IO(ref err) => err.description(),
Error::JoinPathsError(ref err) => err.description(),
Error::MissingCLIInputError(_) => "Missing required CLI argument!",
Error::NetParseError(_) => "Can't parse IP:port",
Error::OfflineArtifactNotFound(_) => "Cached artifact not found in offline mode",
Error::OfflineOriginKeyNotFound(_) => "Cached origin key not found in offline mode",
Expand Down
20 changes: 20 additions & 0 deletions components/common/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ impl EventStreamMetadata {
pub struct AutomateAuthToken(String);

impl AutomateAuthToken {
/// The name of the Clap argument we'll use for arguments of this type.
pub const ARG_NAME: &'static str = "EVENT_STREAM_TOKEN";
// Ideally, we'd like to take advantage of
// `habitat_core::env::Config` trait, but that currently requires
// a `Default` implementation, and there isn't really a legitimate
Expand All @@ -125,6 +127,24 @@ impl AutomateAuthToken {
println!("getting automate auth token from env...");
Ok(env::var(AutomateAuthToken::ENVVAR)?.parse().unwrap())
}

/// Ensure that user input from Clap can be converted an instance
/// of a token.
#[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
pub fn validate(value: String) -> result::Result<(), String> {
value.parse::<Self>()
.map(|_| ())
.map_err(|_| "This should be impossible".to_string())
}

/// Create an instance of `AutomateAuthToken` from validated
/// user input.
pub fn from_matches(m: &ArgMatches) -> result::Result<Self, Error> {
m.value_of(Self::ARG_NAME)
// This should be a required argument
.ok_or_else(|| Error::MissingCLIInputError(Self::ARG_NAME.to_string()))?
.parse()
}
}

impl FromStr for AutomateAuthToken {
Expand Down
72 changes: 69 additions & 3 deletions components/hab/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use habitat_common::{cli::{BINLINK_DIR_ENVVAR,
PACKAGE_TARGET_ENVVAR,
RING_ENVVAR,
RING_KEY_ENVVAR},
types::{EventStreamMetadata,
types::{AutomateAuthToken,
EventStreamMetadata,
ListenCtlAddr},
FeatureFlag};
use habitat_core::{crypto::{keys::PairType,
Expand Down Expand Up @@ -1200,6 +1201,14 @@ fn maybe_add_event_stream_options(mut app: App<'static, 'static>,
.required(true)
.takes_value(true)
.validator(non_empty));
app = app.arg(Arg::with_name(AutomateAuthToken::ARG_NAME).help("An authentication token for \
streaming events to an \
Automate server.")
.long("event-stream-token")
.required(true)
.takes_value(true)
.validator(AutomateAuthToken::validate)
.env(AutomateAuthToken::ENVVAR));
app =
app.arg(Arg::with_name(EventStreamMetadata::ARG_NAME).help("An arbitrary key-value pair \
to add to each event \
Expand Down Expand Up @@ -1421,7 +1430,7 @@ mod tests {
}

#[test]
fn run_requries_app_and_env() {
fn run_requries_app_and_env_and_token() {
let matches = sub_sup_run(event_stream_enabled()).get_matches_from_safe(vec!["run"]);
assert!(matches.is_err());
assert_eq!(matches.unwrap_err().kind,
Expand All @@ -1433,18 +1442,22 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_ok());
}

#[test]
fn app_and_env_options_require_event_stream_feature() {
fn app_and_env_and_token_options_require_event_stream_feature() {
let matches = sub_sup_run(no_feature_flags()).get_matches_from_safe(vec![
"run",
"--event-stream-application",
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
Expand All @@ -1460,6 +1473,8 @@ mod tests {
"--event-stream-application",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
Expand All @@ -1476,6 +1491,8 @@ mod tests {
"",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
Expand All @@ -1489,6 +1506,8 @@ mod tests {
"--event-stream-application",
"MY_APP",
"--event-stream-environment",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
Expand All @@ -1505,6 +1524,8 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
Expand All @@ -1519,6 +1540,8 @@ mod tests {
"foo=bar",
"--event-stream-application",
"MY_APP",
"--event-stream-token",
"MY_TOKEN",
"--event-stream-environment",
"MY_ENV",
]);
Expand All @@ -1542,6 +1565,8 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_ok());
let matches = matches.unwrap();
Expand All @@ -1560,6 +1585,8 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
assert_eq!(matches.unwrap_err().kind, clap::ErrorKind::EmptyValue);
Expand All @@ -1575,6 +1602,8 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
assert_eq!(matches.unwrap_err().kind, clap::ErrorKind::ValueValidation);
Expand All @@ -1590,6 +1619,8 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
assert_eq!(matches.unwrap_err().kind, clap::ErrorKind::ValueValidation);
Expand All @@ -1605,10 +1636,45 @@ mod tests {
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"MY_TOKEN",
]);
assert!(matches.is_err());
assert_eq!(matches.unwrap_err().kind, clap::ErrorKind::ValueValidation);
}

#[test]
fn token_option_must_take_a_value() {
let matches = sub_sup_run(event_stream_enabled()).get_matches_from_safe(vec![
"run",
"--event-stream-application",
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
assert_eq!(error.kind, clap::ErrorKind::EmptyValue);
assert_eq!(error.info,
Some(vec![AutomateAuthToken::ARG_NAME.to_string()]));
}

#[test]
fn token_option_cannot_be_empty() {
let matches = sub_sup_run(event_stream_enabled()).get_matches_from_safe(vec![
"run",
"--event-stream-application",
"MY_APP",
"--event-stream-environment",
"MY_ENV",
"--event-stream-token",
"",
]);
assert!(matches.is_err());
let error = matches.unwrap_err();
assert_eq!(error.kind, clap::ErrorKind::ValueValidation);
}

}
}
16 changes: 9 additions & 7 deletions components/sup/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ lazy_static! {
/// server. Stashes the handle to the stream, as well as the core
/// event information that will be a part of all events, in a global
/// static reference for access later.
pub fn init_stream(conn_info: EventConnectionInfo, event_core: EventCore) {
pub fn init_stream(config: EventStreamConfig, event_core: EventCore) {
INIT.call_once(|| {
println!("automate auth token is {}", conn_info.auth_token);
let conn_info = EventConnectionInfo::new(config.token);
let event_stream = init_nats_stream(conn_info).expect("Could not start NATS thread");
EVENT_STREAM.set(event_stream);
EVENT_CORE.set(event_core);
Expand All @@ -87,6 +87,7 @@ pub struct EventStreamConfig {
environment: String,
application: String,
meta: EventStreamMetadata,
token: AutomateAuthToken,
}

impl EventStreamConfig {
Expand All @@ -98,7 +99,8 @@ impl EventStreamConfig {
application: m.value_of("EVENT_STREAM_APPLICATION")
.map(str::to_string)
.expect("Required option for EventStream feature"),
meta: EventStreamMetadata::from_matches(m)?, })
meta: EventStreamMetadata::from_matches(m)?,
token: AutomateAuthToken::from_matches(m)?, })
}
}

Expand Down Expand Up @@ -146,12 +148,12 @@ pub struct EventCore {
}

impl EventCore {
pub fn new(config: EventStreamConfig, sys: &Sys) -> Self {
pub fn new(config: &EventStreamConfig, sys: &Sys) -> Self {
EventCore { supervisor_id: sys.member_id.clone(),
ip_address: sys.gossip_listen(),
environment: config.environment,
application: config.application,
meta: config.meta, }
environment: config.environment.clone(),
application: config.application.clone(),
meta: config.meta.clone(), }
}
}

Expand Down
9 changes: 3 additions & 6 deletions components/sup/src/manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ use crate::{census::{CensusRing,
Result,
SupError},
event::{self,
EventConnectionInfo,
EventCore,
EventStreamConfig},
http_gateway,
Expand All @@ -66,8 +65,7 @@ use habitat_butterfly::{member::Member,
Suitability},
trace::Trace};
use habitat_common::{outputln,
types::{AutomateAuthToken,
ListenCtlAddr},
types::ListenCtlAddr,
FeatureFlag};
use habitat_core::{crypto::SymKey,
env::{self,
Expand Down Expand Up @@ -488,11 +486,10 @@ impl Manager {
let es_config =
cfg.event_stream_config
.expect("Config should be present if the EventStream feature is enabled");
let ec = EventCore::new(es_config, &sys);
let ec = EventCore::new(&es_config, &sys);
// unwrap won't fail here; if there were an issue, from_env()
// would have already propagated an error up the stack.
event::init_stream(EventConnectionInfo::new(AutomateAuthToken::from_env().unwrap()),
ec);
event::init_stream(es_config, ec);
}

Ok(Manager { state: Arc::new(ManagerState { cfg: cfg_static,
Expand Down

0 comments on commit 74f255a

Please sign in to comment.