Skip to content

Instantly share code, notes, and snippets.

@zeenix
Created November 24, 2017 13:55
Show Gist options
  • Save zeenix/34a25ddaf178a38f76905969a343c7cd to your computer and use it in GitHub Desktop.
Save zeenix/34a25ddaf178a38f76905969a343c7cd to your computer and use it in GitHub Desktop.
WIP work to use failure in parts of habitat
// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use base64::DecodeError;
use std::process::ExitStatus;
use std::fmt;
use std::io;
use std::result;
use std::string::FromUtf8Error;
use rusoto_ecr::GetAuthorizationTokenError;
use common;
use hab;
use handlebars;
use hcore;
use failure;
pub type Result<T> = result::Result<T, failure::Error>;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "{}", _0)]
Base64DecodeError(DecodeError),
#[fail(display = "Docker build failed with exit code: {}", _0)]
BuildFailed(ExitStatus),
#[fail(display = "Could not determine Docker image ID for image: {}", _0)]
DockerImageIdNotFound(String),
#[fail(display = "{}", _0)]
InvalidToken(FromUtf8Error),
#[fail(display = "{}", _0)]
Hab(hab::error::Error),
#[fail(display = "{}", _0)]
HabitatCommon(common::Error),
#[fail(display = "{}", _0)]
HabitatCore(hcore::Error),
#[fail(display = "Docker login failed with exit code: {}", _0)]
LoginFailed(ExitStatus),
#[fail(display = "Docker logout failed with exit code: {}", _0)]
LogoutFailed(ExitStatus),
#[fail(display = "No ECR Tokens returned")]
NoECRTokensReturned,
#[fail(display = "{}", _0)]
TokenFetchFailed(GetAuthorizationTokenError),
#[fail(display = "A primary service package could not be determined from: {:?}. \
At least one package with a run hook must be provided.",
_0)]
PrimaryServicePackageNotFound(Vec<String>),
#[fail(display = "Docker image push failed with exit code: {}", _0)]
PushImageFailed(ExitStatus),
#[fail(display = "Removing Docker local images failed with exit code: {}", _0)]
RemoveImageFailed(ExitStatus),
#[fail(display = "{}", _0)]
TemplateRenderError(handlebars::TemplateRenderError),
#[fail(display = "{}", _0)]
IO(io::Error),
}
// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#[macro_use]
extern crate clap;
extern crate hab;
extern crate habitat_core as hcore;
extern crate habitat_common as common;
extern crate handlebars;
extern crate rusoto_core;
extern crate rusoto_ecr;
extern crate rusoto_credential as aws_creds;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_json;
extern crate tempdir;
extern crate base64;
extern crate url;
extern crate failure;
#[macro_use]
extern crate failure_derive;
mod build;
pub mod cli;
mod docker;
mod error;
mod fs;
pub mod rootfs;
mod util;
use common::ui::UI;
use aws_creds::StaticProvider;
use rusoto_core::Region;
use rusoto_core::request::*;
use rusoto_ecr::{Ecr, EcrClient, GetAuthorizationTokenRequest};
pub use cli::Cli;
pub use build::BuildSpec;
pub use docker::{DockerImage, DockerBuildRoot};
pub use error::{Error, Result};
/// The version of this library and program when built.
pub const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));
/// The Habitat Package Identifier string for a Busybox package.
const BUSYBOX_IDENT: &'static str = "core/busybox-static";
/// The Habitat Package Identifier string for SSL certificate authorities (CA) certificates package.
const CACERTS_IDENT: &'static str = "core/cacerts";
/// An image naming policy.
///
/// This is a value struct which captures the naming and tagging intentions for an image.
#[derive(Debug)]
pub struct Naming<'a> {
/// An optional custom image name which would override a computed default value.
pub custom_image_name: Option<&'a str>,
/// Whether or not to tag the image with a latest value.
pub latest_tag: bool,
/// Whether or not to tag the image with a value containing a version from a Package
/// Identifier.
pub version_tag: bool,
/// Whether or not to tag the image with a value containing a version and release from a
/// Package Identifier.
pub version_release_tag: bool,
/// An optional custom tag value for the image.
pub custom_tag: Option<&'a str>,
/// A URL to a custom Docker registry to publish to. This will be used as part of every tag
/// before pushing.
pub registry_url: Option<&'a str>,
/// The type of registry we're publishing to. Ex: Amazon, Docker, Google, Azure.
pub registry_type: &'a str,
}
impl<'a> Naming<'a> {
/// Creates a `Naming` from cli arguments.
pub fn new_from_cli_matches(m: &'a clap::ArgMatches) -> Self {
let registry_type = m.value_of("REGISTRY_TYPE").unwrap_or("docker");
let registry_url = m.value_of("REGISTRY_URL");
Naming {
custom_image_name: m.value_of("IMAGE_NAME"),
latest_tag: !m.is_present("NO_TAG_LATEST"),
version_tag: !m.is_present("NO_TAG_VERSION"),
version_release_tag: !m.is_present("NO_TAG_VERSION_RELEASE"),
custom_tag: m.value_of("TAG_CUSTOM"),
registry_url: registry_url,
registry_type: registry_type,
}
}
}
/// A credentials username and password pair.
///
/// This is a value struct which references username and password values.
#[derive(Debug)]
pub struct Credentials {
pub username: String,
pub password: String,
}
impl Credentials {
pub fn new(registry_type: &str, username: &str, password: &str) -> Result<Self> {
match registry_type {
"amazon" => {
// The username and password should be valid IAM credentials
let provider =
StaticProvider::new_minimal(username.to_string(), password.to_string());
// TODO TED: Make the region configurable
let client =
EcrClient::new(default_tls_client().unwrap(), provider, Region::UsWest2);
let auth_token_req = GetAuthorizationTokenRequest { registry_ids: None };
let token = match client.get_authorization_token(&auth_token_req) {
Ok(resp) => {
match resp.authorization_data {
Some(auth_data) => auth_data[0].clone().authorization_token.unwrap(),
None => return Err(Error::NoECRTokensReturned.into()),
}
}
Err(e) => return Err(Error::TokenFetchFailed(e).into()),
};
let creds: Vec<String> = match base64::decode(&token) {
Ok(decoded_token) => {
match String::from_utf8(decoded_token) {
Ok(dts) => dts.split(':').map(String::from).collect(),
Err(err) => return Err(Error::InvalidToken(err).into()),
}
}
Err(err) => return Err(Error::Base64DecodeError(err).into()),
};
Ok(Credentials {
username: creds[0].to_string(),
password: creds[1].to_string(),
})
}
_ => {
Ok(Credentials {
username: username.to_string(),
password: password.to_string(),
})
}
}
}
}
/// Exports a Docker image to a Docker engine from a build specification and naming policy.
///
/// # Errors
///
/// * If a generic and temporary build root directory cannot be created containing a root
/// file system
/// * If additional Docker-related files cannot be created in the root file system
/// * If building the Docker image fails
/// * If destroying the temporary build root directory fails
pub fn export(ui: &mut UI, build_spec: BuildSpec, naming: &Naming) -> Result<DockerImage> {
ui.begin(format!(
"Building a runnable Docker image with: {}",
build_spec.idents_or_archives.join(", ")
))?;
let build_root = DockerBuildRoot::from_build_root(build_spec.create(ui)?, ui)?;
let image = build_root.export(ui, naming)?;
build_root.destroy(ui)?;
ui.end(format!(
"Docker image '{}' created with tags: {}",
image.name(),
image.tags().join(", ")
))?;
Ok(image)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment