Skip to content

Commit

Permalink
Merge pull request #7133 from habitat-sh/jeremymv2/bulkupload_create_…
Browse files Browse the repository at this point in the history
…origins

hab pkg bulkupload origin creation option
  • Loading branch information
jeremymv2 authored Nov 12, 2019
2 parents 2ff916d + 33b7be3 commit db6f4d1
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 31 deletions.
18 changes: 18 additions & 0 deletions components/builder-api-client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,24 @@ impl BuilderAPIProvider for BuilderAPIClient {
.ok_if(&[StatusCode::NO_CONTENT])
}

/// Check an origin exists
///
/// # Failures
///
/// * Origin is not found
/// * Remote Builder is not available
fn check_origin(&self, origin: &str, token: &str) -> Result<()> {
debug!("Checking for existence of origin: {}", origin);

let path = format!("depot/origins/{}", origin);

self.0
.get(&path)
.bearer_auth(token)
.send()?
.ok_if(&[StatusCode::OK])
}

/// Delete an origin
///
/// # Failures
Expand Down
2 changes: 2 additions & 0 deletions components/builder-api-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ pub trait BuilderAPIProvider: Sync + Send {

fn delete_origin_secret(&self, origin: &str, token: &str, key: &str) -> Result<()>;

fn check_origin(&self, origin: &str, token: &str) -> Result<()>;

fn delete_origin(&self, origin: &str, token: &str) -> Result<()>;

fn list_origin_secrets(&self, origin: &str, token: &str) -> Result<Vec<String>>;
Expand Down
2 changes: 2 additions & 0 deletions components/common/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ pub enum Status {
Demoted,
Demoting,
Determining,
Discovering,
Downloading,
DryRunDeleting,
Encrypting,
Expand Down Expand Up @@ -241,6 +242,7 @@ impl Status {
Status::Demoted => (Glyph::CheckMark, "Demoted".into(), Color::Info),
Status::Demoting => (Glyph::RightArrow, "Demoting".into(), Color::Info),
Status::Determining => (Glyph::Cloud, "Determining".into(), Color::Info),
Status::Discovering => (Glyph::Cloud, "Discovering".into(), Color::Info),
Status::Downloading => (Glyph::DownArrow, "Downloading".into(), Color::Info),
Status::DryRunDeleting => {
(Glyph::BoxedX, "Would be deleted (Dry run)".into(), Color::Critical)
Expand Down
5 changes: 4 additions & 1 deletion components/hab/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,10 @@ pub fn get(feature_flags: FeatureFlag) -> App<'static, 'static> {
of the value of this option.")
(@arg FORCE: --force "Skip checking availability of package and \
force uploads, potentially overwriting a stored copy of a package.")
(@arg AUTO_BUILD: --("auto-build") "Enable auto-build for all packages in this upload. Only applicable to SaaS Builder.")
(@arg AUTO_BUILD: --("auto-build") "Enable auto-build for all packages in this upload. \
Only applicable to SaaS Builder.")
(@arg AUTO_CREATE_ORIGINS: --("auto-create-origins") "Skip the confirmation prompt and \
automatically create origins that do not exist in the target Builder.")
(@arg UPLOAD_DIRECTORY: +required {dir_exists}
"Directory Path from which artifacts will be uploaded.")
)
Expand Down
1 change: 1 addition & 0 deletions components/hab/src/command/pkg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod binlink;
pub mod build;
pub mod bulkupload;
pub mod channels;
pub mod delete;
pub mod demote;
Expand Down
128 changes: 128 additions & 0 deletions components/hab/src/command/pkg/bulkupload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//! Uploads packages from cache to a [Depot](../depot).
//!
//! # Examples
//!
//! ```bash
//! $ hab pkg bulkupload /path/to/artifact_download_dir \
//! -u http://localhost:9632
//! ```
//!
//! Will upload all packages in cache to Builder.
use crate::{api_client::{self,
BuildOnUpload,
Client},
command,
common::ui::{Glyph,
Status,
UIReader,
UIWriter,
UI},
error::{Error,
Result},
hcore::{package::PackageArchive,
ChannelIdent},
PRODUCT,
VERSION};
use glob::glob_with;
use reqwest::StatusCode;
use std::{collections::BTreeSet,
path::{Path,
PathBuf}};

/// Bulk Upload the packages from the cache to a Depot.
///
/// # Failures
///
/// * Fails if it cannot create a missing origin
/// * Fails if it cannot upload the artifact
#[allow(clippy::too_many_arguments)]
pub fn start(ui: &mut UI,
bldr_url: &str,
additional_release_channel: &Option<ChannelIdent>,
token: &str,
artifact_path: &Path,
force_upload: bool,
auto_build: BuildOnUpload,
auto_create_origins: bool,
key_path: &Path)
-> Result<()> {
const OPTIONS: glob::MatchOptions = glob::MatchOptions { case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: true, };
let artifact_paths =
vec_from_glob_with(&artifact_path.join("*.hart").display().to_string(), OPTIONS);

ui.begin(format!("Preparing to upload artifacts to the '{}' channel on {}",
additional_release_channel.clone()
.unwrap_or_else(ChannelIdent::unstable),
bldr_url))?;
ui.status(Status::Using,
format!("{} for artifacts and {} for signing keys",
&artifact_path.display(),
key_path.display()))?;
ui.status(Status::Found,
format!("{} artifact(s) for upload.", artifact_paths.len()))?;
ui.status(Status::Discovering,
String::from("origin names from local artifact cache"))?;

let mut origins = BTreeSet::new();
for artifact_path in &artifact_paths {
let ident = PackageArchive::new(&artifact_path).ident()?;
origins.insert(ident.origin);
}
let mut origins_to_create: Vec<String> = Vec::new();
let api_client = Client::new(bldr_url, PRODUCT, VERSION, None)?;

for origin in origins {
match api_client.check_origin(&origin, token) {
Ok(()) => {
ui.status(Status::Custom(Glyph::CheckMark,
format!("Origin '{}' already exists", &origin)),
String::from(""))?;
}
Err(api_client::Error::APIError(StatusCode::NOT_FOUND, _)) => {
ui.status(Status::Missing, format!("origin '{}'", &origin))?;
origins_to_create.push(origin);
}
Err(err) => return Err(Error::from(err)),
}
}

if !origins_to_create.is_empty() {
if !auto_create_origins {
ui.warn(String::from("Origins are required for uploading the artifacts. The \
Builder account that creates the origin is the owner."))?;
if !ask_create_origins(ui)? {
return Ok(());
};
};
for origin_to_create in origins_to_create {
command::origin::create::start(ui, &bldr_url, &token, &origin_to_create)?;
}
};

for artifact_path in &artifact_paths {
command::pkg::upload::start(ui,
&bldr_url,
&additional_release_channel,
&token,
&artifact_path,
force_upload,
auto_build,
&key_path)?
}

Ok(())
}

fn vec_from_glob_with(pattern: &str, options: glob::MatchOptions) -> Vec<PathBuf> {
glob_with(pattern, options).unwrap()
.map(std::result::Result::unwrap)
.collect()
}

fn ask_create_origins(ui: &mut UI) -> Result<bool> {
Ok(ui.prompt_yes_no("Create the above missing origins under your Builder account?",
Some(true))?)
}
35 changes: 5 additions & 30 deletions components/hab/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use clap::{ArgMatches,
Shell};
use env_logger;
use futures::prelude::*;
use glob::glob_with;
use hab::{cli::{self,
parse_optional_arg},
command::{self,
Expand Down Expand Up @@ -885,36 +884,18 @@ fn sub_pkg_bulkupload(ui: &mut UI, m: &ArgMatches<'_>) -> Result<()> {
} else {
BuildOnUpload::Disable
};
let auto_create_origins = m.is_present("AUTO_CREATE_ORIGINS");
let token = auth_token_param_or_env(m)?;
const OPTIONS: glob::MatchOptions = glob::MatchOptions { case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: true, };
let artifact_paths =
vec_from_glob_with(&artifact_path.join("*.hart").display().to_string(), OPTIONS);

ui.begin(format!("Preparing to upload artifacts to the '{}' channel on {}",
additional_release_channel.clone()
.unwrap_or_else(ChannelIdent::unstable),
url))?;
ui.status(Status::Using,
format!("{} for artifacts and {} for signing keys.",
&artifact_path.display(),
key_path.display()))?;
ui.status(Status::Found,
format!("{} artifacts for upload.", artifact_paths.len()))?;

for artifact_path in artifact_paths {
command::pkg::upload::start(ui,

command::pkg::bulkupload::start(ui,
&url,
&additional_release_channel,
&token,
&artifact_path,
force_upload,
auto_build,
&key_path)?;
}

Ok(())
auto_create_origins,
&key_path)
}

fn sub_pkg_upload(ui: &mut UI, m: &ArgMatches<'_>) -> Result<()> {
Expand Down Expand Up @@ -1841,12 +1822,6 @@ fn bulkupload_dir_from_matches(matches: &ArgMatches<'_>) -> PathBuf {
.expect("CLAP-validated upload dir")
}

fn vec_from_glob_with(pattern: &str, options: glob::MatchOptions) -> Vec<PathBuf> {
glob_with(pattern, options).unwrap()
.map(std::result::Result::unwrap)
.collect()
}

/// A Builder URL, but *only* if the user specified it via CLI args or
/// the environment
fn bldr_url_from_input(m: &ArgMatches<'_>) -> Option<String> {
Expand Down

0 comments on commit db6f4d1

Please sign in to comment.