Skip to content

Commit

Permalink
Add metrics queries; have prep-mail read report from URL (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
avranju authored Sep 10, 2018
1 parent ca1de34 commit 3afce1b
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 23 deletions.
3 changes: 3 additions & 0 deletions tools/snitch/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target/
.vscode/
scripts/
5 changes: 5 additions & 0 deletions tools/snitch/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions tools/snitch/prep-mail/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ publish = false

[dependencies]
env_logger = "0.5"
futures = "0.1"
handlebars = "1.0"
hyper = "0.12"
hyper-tls = "0.3.0"
serde = "1.0"
serde_json = "1.0"
tokio = "0.1"
url = "1.7"

snitcher = { path = "../snitcher" }
2 changes: 1 addition & 1 deletion tools/snitch/prep-mail/docker/linux/amd64/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM ekidd/rust-musl-builder AS builder
ARG SRC_DIR=.

# Add source code
COPY $EXE_DIR/ ./
COPY $SRC_DIR/ ./

# Fix permissions on source code.
RUN sudo chown -R rust:rust /home/rust
Expand Down
25 changes: 25 additions & 0 deletions tools/snitch/prep-mail/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
use std::fmt;

use handlebars::TemplateRenderError;
use hyper_tls::Error as HyperTlsError;
use serde_json::Error as SerdeJsonError;
use snitcher::error::Error as SnitcherError;
use url::ParseError as ParseUrlError;

pub type Result<T> = ::std::result::Result<T, Error>;

Expand All @@ -12,6 +15,10 @@ pub enum Error {
Env(String),
SerdeJson(SerdeJsonError),
Handlebars(TemplateRenderError),
HyperTls(HyperTlsError),
ParseUrl(ParseUrlError),
Snitcher(SnitcherError),
NoReportJsonFound,
}

impl fmt::Display for Error {
Expand All @@ -31,3 +38,21 @@ impl From<TemplateRenderError> for Error {
Error::Handlebars(err)
}
}

impl From<ParseUrlError> for Error {
fn from(err: ParseUrlError) -> Error {
Error::ParseUrl(err)
}
}

impl From<HyperTlsError> for Error {
fn from(err: HyperTlsError) -> Error {
Error::HyperTls(err)
}
}

impl From<SnitcherError> for Error {
fn from(err: SnitcherError) -> Error {
Error::Snitcher(err)
}
}
72 changes: 62 additions & 10 deletions tools/snitch/prep-mail/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,92 @@
// Copyright (c) Microsoft. All rights reserved.

#![deny(warnings)]

extern crate env_logger;
extern crate futures;
extern crate handlebars;
extern crate hyper;
extern crate hyper_tls;
extern crate serde;
extern crate serde_json;
extern crate snitcher;
extern crate tokio;
extern crate url;

use std::env;
use std::sync::{Arc, Mutex};

use futures::future::{self, Either};
use futures::Future;
use handlebars::Handlebars;
use hyper::{Client as HyperClient, Method};
use hyper_tls::HttpsConnector;
use snitcher::client;
use snitcher::connect::HyperClientService;
use snitcher::report::Report;
use url::Url;

mod error;

use error::{Error, Result};
use snitcher::report::Report;

const REPORT_JSON_KEY: &str = "REPORT_JSON";
const REPORT_JSON_URL_KEY: &str = "REPORT_JSON_URL";
const REPORT_TEMPLATE_KEY: &str = "REPORT_TEMPLATE";
const DEFAULT_REPORT_TEMPLATE: &str = include_str!("mail-template.hbs");

fn main() -> Result<()> {
env_logger::init();

// read the report JSON from the environment
let report_json = get_env(REPORT_JSON_KEY)?;
let report: Report = serde_json::from_str(&report_json)?;
let template =
get_env(REPORT_TEMPLATE_KEY).unwrap_or_else(|_| String::from(DEFAULT_REPORT_TEMPLATE));
let report_url = Url::parse(&get_env(REPORT_JSON_URL_KEY)?)?;

let task = get_report_json(report_url)
.and_then(|report_json| report_json.ok_or(Error::NoReportJsonFound))
.and_then(|report_json| serde_json::from_str(&report_json).map_err(Error::from))
.and_then(|report: Report| {
let template = get_env(REPORT_TEMPLATE_KEY)
.unwrap_or_else(|_| String::from(DEFAULT_REPORT_TEMPLATE));

// render the template and generate report
let reg = Handlebars::new();
reg.render_template(&template, &report).map_err(Error::from)
})
.map(|html| println!("{}", html));

// render the template and generate report
let reg = Handlebars::new();
println!("{}", reg.render_template(&template, &report)?);
let error = Arc::new(Mutex::new(None));
let error_copy = error.clone();
tokio::run(task.map_err(move |err| {
*error_copy.lock().unwrap() = Some(err);
}));

Ok(())
let lock = Arc::try_unwrap(error).expect("Error lock still has multiple owners.");
let error = lock.into_inner().expect("Error mutex cannot be locked.");

// we want to propagate any errors we might have encountered from 'main'
// because we want to exit with a non-zero error code when something goes
// wrong
Ok(error.map(|err| Err(err)).unwrap_or_else(|| Ok(()))?)
}

fn get_env(key: &str) -> Result<String> {
env::var(key).map_err(|_| Error::Env(key.to_string()))
}

fn get_report_json(report_url: Url) -> impl Future<Item = Option<String>, Error = Error> + Send {
HttpsConnector::new(4)
.map(|connector| {
let path = report_url.path().to_owned();
let client = client::Client::new(
HyperClientService::new(HyperClient::builder().build(connector)),
report_url,
);

Either::A(
client
.request_str::<()>(Method::GET, &path, None, None, false)
.map_err(Error::from),
)
})
.map_err(Error::from)
.unwrap_or_else(|err| Either::B(future::err(err)))
}
180 changes: 180 additions & 0 deletions tools/snitch/scripts/buildImage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/bin/bash

set -e

SCRIPT_NAME=$(basename "$0")
DIR=$(cd "$(dirname "$0")"/.. && pwd)
DEFAULT_DOCKER_NAMESPACE="microsoft"
DOCKER_NAMESPACE=$DEFAULT_DOCKER_NAMESPACE
DOCKERFILE=
SKIP_PUSH=0

usage()
{
echo "$SCRIPT_NAME [options]"
echo "Note: Depending on the options you might have to run this as root or sudo."
echo ""
echo "options"
echo " -i, --image-name Image name (e.g. snitcher)"
echo " -P, --project Project to build image for. Must be 'snitcher' or 'prep-mail'"
echo " -r, --registry Docker registry required to build, tag and run the module"
echo " -u, --username Docker Registry Username"
echo " -p, --password Docker Username's password"
echo " -n, --namespace Docker namespace (default: $DEFAULT_DOCKER_NAMESPACE)"
echo " -v, --image-version Docker Image Version."
echo "--skip-push Build images, but don't push them"
exit 1;
}

print_help_and_exit()
{
echo "Run $SCRIPT_NAME --help for more information."
exit 1
}

process_args()
{
save_next_arg=0
for arg in "$@"
do
if [ $save_next_arg -eq 1 ]; then
DOCKER_REGISTRY="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 2 ]; then
DOCKER_USERNAME="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 3 ]; then
DOCKER_PASSWORD="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 4 ]; then
DOCKER_IMAGEVERSION="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 5 ]; then
PROJECT="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 6 ]; then
DOCKER_IMAGENAME="$arg"
save_next_arg=0
elif [ $save_next_arg -eq 7 ]; then
DOCKER_NAMESPACE="$arg"
save_next_arg=0
else
case "$arg" in
"-h" | "--help" ) usage;;
"-r" | "--registry" ) save_next_arg=1;;
"-u" | "--username" ) save_next_arg=2;;
"-p" | "--password" ) save_next_arg=3;;
"-v" | "--image-version" ) save_next_arg=4;;
"-P" | "--project" ) save_next_arg=5;;
"-i" | "--image-name" ) save_next_arg=6;;
"-n" | "--namespace" ) save_next_arg=7;;
"--skip-push" ) SKIP_PUSH=1 ;;
* ) usage;;
esac
fi
done

if [[ -z ${DOCKER_REGISTRY} ]]; then
echo "Registry parameter invalid"
print_help_and_exit
fi

if [[ $SKIP_PUSH -eq 0 ]]; then
if [[ -z ${DOCKER_USERNAME} ]]; then
echo "Docker username parameter invalid"
print_help_and_exit
fi

if [[ -z ${DOCKER_PASSWORD} ]]; then
echo "Docker password parameter invalid"
print_help_and_exit
fi
fi

if [[ -z ${DOCKER_IMAGENAME} ]]; then
echo "Docker image name parameter invalid"
print_help_and_exit
fi

if [[ -z ${DOCKER_IMAGEVERSION} ]]; then
echo "Docker image version not found."
print_help_and_exit
fi

DOCKERFILE="$DIR/$PROJECT/docker/linux/amd64/Dockerfile"
if [[ ! -f $DOCKERFILE ]]; then
echo "No Dockerfile at $DOCKERFILE"
print_help_and_exit
fi
}

###############################################################################
# Build docker image and push it to private repo
#
# @param[1] - imagename; Name of the docker edge image to publish; Required;
# @param[2] - arch; Arch of base image; Required;
# @param[3] - dockerfile; Path to the dockerfile; Optional;
# Leave as "" and defaults will be chosen.
# @param[4] - context_path; docker context path; Required;
# @param[5] - build_args; docker context path; Optional;
# Leave as "" and no build args will be supplied.
###############################################################################
docker_build_and_tag_and_push()
{
imagename="$1"
arch="$2"
dockerfile="$3"
context_path="$4"
build_args="${*:5}"

if [ -z "${imagename}" ] || [ -z "${arch}" ] || [ -z "${context_path}" ]; then
echo "Error: Arguments are invalid [$imagename] [$arch] [$context_path]"
exit 1
fi

echo "Building and pushing Docker image $imagename for $arch"
docker_build_cmd="docker build --no-cache"
docker_build_cmd+=" -t $DOCKER_REGISTRY/$DOCKER_NAMESPACE/$imagename:$DOCKER_IMAGEVERSION-linux-$arch"
if [ ! -z "${dockerfile}" ]; then
docker_build_cmd+=" --file $dockerfile"
fi
docker_build_cmd+=" $build_args $context_path"

echo "Running... $docker_build_cmd"

if ! $docker_build_cmd; then
echo "Docker build failed with exit code $?"
exit 1
fi

if [ $SKIP_PUSH -eq 0 ]; then
docker_push_cmd="docker push $DOCKER_REGISTRY/$DOCKER_NAMESPACE/$imagename:$DOCKER_IMAGEVERSION-linux-$arch"
echo "Running... $docker_push_cmd"
if ! $docker_push_cmd; then
echo "Docker push failed with exit code $?"
exit 1
fi
fi
}

process_args "$@"

# log in to container registry
if [ $SKIP_PUSH -eq 0 ]; then
if ! docker login "$DOCKER_REGISTRY" -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"; then
echo "Docker login failed!"
exit 1
fi
fi

# push image
docker_build_and_tag_and_push \
"$DOCKER_IMAGENAME" \
"amd64" \
"$DOCKERFILE" \
"$DIR" \
""

echo "Done building and pushing Docker image $DOCKER_IMAGENAME for $PROJECT"

exit $?
2 changes: 1 addition & 1 deletion tools/snitch/snitcher/docker/linux/amd64/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM ekidd/rust-musl-builder AS builder
ARG SRC_DIR=.

# Add source code
COPY $EXE_DIR/ ./
COPY $SRC_DIR/ ./

# Fix permissions on source code.
RUN sudo chown -R rust:rust /home/rust
Expand Down
Loading

0 comments on commit 3afce1b

Please sign in to comment.