Skip to content

Commit

Permalink
Enable launches using binaries stored in a private S3 bucket
Browse files Browse the repository at this point in the history
  • Loading branch information
asdaraujo committed Feb 19, 2020
1 parent 76e7431 commit c43014a
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 108 deletions.
2 changes: 2 additions & 0 deletions setup/terraform/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*.signed
*.urls
*tfstate*
.cdsw
.cem
Expand Down
2 changes: 1 addition & 1 deletion setup/terraform/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RUN apk update && apk upgrade \
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN pip install --upgrade pip \
&& pip install --no-cache jinja2 pyyaml
&& pip install --no-cache jinja2 pyyaml boto3

RUN wget https://releases.hashicorp.com/terraform/${TERRAFORMVERSION}/terraform_${TERRAFORMVERSION}_linux_amd64.zip \
&& unzip terraform_${TERRAFORMVERSION}_linux_amd64.zip \
Expand Down
28 changes: 14 additions & 14 deletions setup/terraform/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ except:
print("ERROR: Python module \"Jinja2\" not found.")
missing_modules.append("jinja2")
try:
import boto3
except:
print("ERROR: Python module \"Boto3\" not found.")
missing_modules.append("boto3")
if missing_modules:
print("Please install the missing modules with the command below and try again:")
print("\n pip install " + " ".join(missing_modules) + "\n")
Expand Down Expand Up @@ -261,9 +267,14 @@ function check_file_staleness() {
echo $stale
}

function check_config() {
local template_file=$1
local compare_file=$2
function presign_urls() {
local stack_file=$1
python $BASE_DIR/presign_urls.py "$stack_file"
}

function validate_env() {
local template_file=$BASE_DIR/.env.template
local compare_file=$(get_env_file_path $NAMESPACE)
if [ ! -f $template_file ]; then
echo "ERROR: Cannot find the template file $template_file." > /dev/stderr
exit 1
Expand All @@ -280,17 +291,6 @@ EOF
fi
}

function check_all_configs() {
local stack
check_config $BASE_DIR/.env.template $(get_env_file_path $NAMESPACE)
if [ -e $BASE_DIR/resources/stack.${NAMESPACE}.sh ]; then
stack=$BASE_DIR/resources/stack.${NAMESPACE}.sh
else
stack=$BASE_DIR/resources/stack.sh
fi
check_config $BASE_DIR/resources/stack.template-cdp.sh $stack
}

function kerb_auth_for_cluster() {
local cluster_id=$1
local public_dns=$(public_dns $cluster_id)
Expand Down
7 changes: 4 additions & 3 deletions setup/terraform/instances.tf
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ resource "aws_instance" "cluster" {

provisioner "remote-exec" {
inline = [
"set -u",
"set -e",
"set -o nounset",
"set -o errexit",
"set -o pipefail",
"sudo mkdir -p /opt/dataloader/",
"sudo cp /tmp/smm/* /opt/dataloader/",
"sudo chmod 755 /opt/dataloader/*.sh",
"chmod +x /tmp/resources/*sh",
"sudo bash -x /tmp/resources/setup.sh aws \"${var.ssh_username}\" \"${var.ssh_password}\" \"${var.namespace}\"",
"sudo bash -x /tmp/resources/setup.sh aws \"${var.ssh_username}\" \"${var.ssh_password}\" \"${var.namespace}\" 2>&1 | tee /tmp/resources/setup.log",
]

connection {
Expand Down
16 changes: 14 additions & 2 deletions setup/terraform/launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ if [ $# != 1 ]; then
exit 1
fi
NAMESPACE=$1
validate_env
load_env $NAMESPACE
check_all_configs
check_python_modules

# Check if enddate is close
Expand All @@ -35,11 +35,19 @@ elif [ "$DATE_CHECK" -le "$WARNING_THRESHOLD_DAYS" ]; then
fi
fi

START_TIME=$(date +%s)

mkdir -p "${NAMESPACE_DIR}"

# Perform a quick configuration sanity check before calling Terraform
source $BASE_DIR/resources/common.sh
load_stack $NAMESPACE $BASE_DIR/resources local
validate_stack $NAMESPACE $BASE_DIR/resources

# Presign URLs, if needed
STACK_FILE=$(get_stack_file $NAMESPACE $BASE_DIR/resources exclude-signed)
echo "Using stack: $STACK_FILE"
presign_urls $STACK_FILE

log "Validate services selection: $CM_SERVICES"
CLUSTER_HOST=dummy PRIVATE_IP=dummy PUBLIC_DNS=dummy DOCKER_DEVICE=dummy CDSW_DOMAIN=dummy \
python $BASE_DIR/resources/cm_template.py --cdh-major-version $CDH_MAJOR_VERSION $CM_SERVICES --validate-only
Expand Down Expand Up @@ -83,4 +91,8 @@ echo ""
echo "Uploading instance details to Web Server:"
"${BASE_DIR}/upload-instance-details.sh" "${NAMESPACE}"

END_TIME=$(date +%s)
DURATION=$((END_TIME-START_TIME))
log "Deployment completed in $(printf "%d:%02d" "$((DURATION/60))" "$((DURATION%60))") minutes"

) 2>&1 | tee $BASE_DIR/logs/setup.log.${1:-unknown}.$(date +%Y%m%d%H%M%S)
25 changes: 18 additions & 7 deletions setup/terraform/list-details.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@ fi
NAMESPACE=${1:-}

function web_instance() {
cat $TF_JSON_FILE | jq -r '.values[]?.resources[]? | select(.address == "aws_instance.web") | "\(.values.tags.Name) \(.values.public_dns) \(.values.public_ip) \(.values.private_ip)"'
if [ -s $TF_JSON_FILE ]; then
cat $TF_JSON_FILE | jq -r '.values[]?.resources[]? | select(.address == "aws_instance.web") | "\(.values.tags.Name) \(.values.public_dns) \(.values.public_ip) \(.values.private_ip)"'
fi
}

function cluster_instances() {
cat $TF_JSON_FILE | jq -r '.values[]?.resources[]? | select(.address == "aws_instance.cluster") | "\(.values.tags.Name) \(.values.public_dns) \(.values.public_ip) \(.values.private_ip)"'
if [ -s $TF_JSON_FILE ]; then
cat $TF_JSON_FILE | jq -r '.values[]?.resources[]? | select(.address == "aws_instance.cluster") | "\(.values.tags.Name) \(.values.public_dns) \(.values.public_ip) \(.values.private_ip)"'
fi
}

function enddate() {
cat $TF_JSON_FILE | jq -r '.values.root_module.resources[0].values.tags.enddate' | sed 's/null//'
if [ -s $TF_JSON_FILE ]; then
cat $TF_JSON_FILE | jq -r '.values.root_module.resources[0].values.tags.enddate' | sed 's/null//'
fi
}

function show_details() {
Expand All @@ -30,9 +36,13 @@ function show_details() {
load_env $namespace

TF_JSON_FILE=$BASE_DIR/.tf.json.$$
trap "rm -f $TF_JSON_FILE" 0
#trap "rm -f $TF_JSON_FILE" 0

terraform show -json $NAMESPACE_DIR/terraform.state > $TF_JSON_FILE
rm -f $TF_JSON_FILE
mkdir -p $NAMESPACE_DIR
set +e
terraform show -json $NAMESPACE_DIR/terraform.state > $TF_JSON_FILE 2>/dev/null
set +e

web_instance | while read name public_dns public_ip private_ip; do
printf "%-40s %-55s %-15s %-15s\n" "$name" "$public_dns" "$public_ip" "$private_ip"
Expand All @@ -59,7 +69,7 @@ function show_details() {
fi

if [ "$summary_only" != "no" ]; then
printf "%-15s %-40s %10d %8s %9s %s\n" "$namespace" "$web_server" "$(cat $INSTANCE_LIST_FILE | wc -l)" "$enddate" "$remaining_days" "$warning"
printf "%-25s %-40s %10d %8s %9s %s\n" "$namespace" "$web_server" "$(cat $INSTANCE_LIST_FILE | wc -l)" "$enddate" "$remaining_days" "$warning"
else
if [ -s "$TF_VAR_web_ssh_private_key" ]; then
echo "WEB SERVER Key file: $TF_VAR_web_ssh_private_key"
Expand Down Expand Up @@ -110,10 +120,11 @@ function show_details() {
}

if [ "$NAMESPACE" == "" ]; then
printf "%-15s %-40s %10s %8s %9s\n" "Namespace" "Web Server" "# of VMs" "End Date" "Days Left"
printf "%-25s %-40s %10s %8s %9s\n" "Namespace" "Web Server" "# of VMs" "End Date" "Days Left"
for namespace in $(get_namespaces); do
show_details $namespace yes
done
echo ""
echo "${C_YELLOW} To list the full details for a particular namespace, use:"
echo ""
echo " ./list-details.sh <namespace>"
Expand Down
97 changes: 97 additions & 0 deletions setup/terraform/presign_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import boto3
import json
import os
import re
import sys
from subprocess import Popen, PIPE
from tempfile import mkstemp
from botocore.exceptions import ClientError

S3_CLIENT = boto3.client("s3",
aws_access_key_id=os.environ.get("TF_VAR_aws_access_key_id", ""),
aws_secret_access_key=os.environ.get("TF_VAR_aws_secret_access_key", ""))

def create_presigned_url(bucket_name, object_name, expiration=7200):
global S3_CLIENT
response = S3_CLIENT.generate_presigned_url("get_object",
Params={"Bucket": bucket_name,
"Key": object_name},
ExpiresIn=expiration)
return response

def get_file(bucket, key):
s3 = boto3.resource('s3')
_, tempfile_path = mkstemp()
try:
S3_CLIENT.download_file(bucket, key, tempfile_path)
except ClientError as exc:
if key.endswith("/manifest.json") and "Error" in exc.response and "Code" in exc.response["Error"] and exc.response["Error"]["Code"] in ["404", "403"]:
return None
raise
return open(tempfile_path, 'r').read()

def remove_file(file_path):
try:
os.remove(file_path)
except OSError:
pass

def compute_env(file_path):
command = "bash -c 'source %s && set'" % (file_path,)
proc = Popen(command, shell=True, stdout=PIPE)
stdout, _ = proc.communicate()
env = {}
for line in stdout.decode().split("\n"):
line = line.rstrip()
m = re.match(r"^([A-Za-z0-9_]*)=(s3://([^/]*)/(.*))", line)
if m:
var_name, value, bucket, key = m.groups()
env[var_name] = {"value":value, "bucket":bucket, "key":key}
return env

def convert_stack_file(file_path):
env = compute_env(file_path)
output_stack_path = file_path + ".signed"
remove_file(output_stack_path)
output_stack_file = open(output_stack_path, "w")
url_map = {}
converted = False
for line in open(file_path):
line = line.rstrip()
m = re.match(r"(^[^=]*)=", line)
if m:
var_name, = m.groups()
if var_name in env:
converted = True
bucket = env[var_name]["bucket"]
key = env[var_name]["key"]
main_presigned_url = create_presigned_url(bucket, key)
line = "%s=\"%s\"" % (var_name, main_presigned_url)

# Check for manifest.json
manifest_key = key.rstrip("/") + "/manifest.json"
manifest = get_file(bucket, manifest_key)
if manifest:
manifest_presigned_url = create_presigned_url(bucket, manifest_key)
url_map[main_presigned_url + "/manifest.json"] = manifest_presigned_url
j = json.loads(manifest)
for parcel in j["parcels"]:
parcel_key = key.rstrip("/") + "/" + parcel["parcelName"]
parcel_url = main_presigned_url + "/" + parcel["parcelName"]
parcel_presigned_url = create_presigned_url(bucket, parcel_key)
url_map[parcel_url] = parcel_presigned_url

output_stack_file.write(line + "\n")
output_stack_file.close()

output_urls_path = file_path + ".urls"
remove_file(output_urls_path)
if not converted:
remove_file(output_stack_path)
elif url_map:
output_map = open(output_urls_path, "w")
for key in url_map:
output_map.write("%s-->%s\n" % (key, url_map[key]))
output_map.close()

convert_stack_file(sys.argv[1])
Loading

0 comments on commit c43014a

Please sign in to comment.