#!/bin/sh ######################################################## # history ######################################################## # # 0.3 (2011-02-08) # - create/update configuration template 'InitialConfig' instead # of 'Default', which is not editable # # 0.2 (2011-02-03) # - check whether SSH key exists # - create/update configuration template 'Default' # if not explicitly specified. Previously, the # configuration template was named identically to # the environment # # 0.1 (2011-02-03) # - initial release # "global" vars APP= ENV= TMPL= APPFILE= APPDESCR= ENVDESCR= IMAGEID= INSTANCETYPE= VERSION= OPTIONS= KEYNAME= OPTFILE= CNAME= CONTAINER= DEFAULT_TEMPLATE_NAME="InitialConfig" # "Default" is not editable TMPOPTFILE=`mktemp -p "$TMPDIR" launcfg.XXXXXXX` # Beanstalk app parameters MIN_COUNT=1 MAX_COUNT=1 SECGROUP= PARAM1= PARAM2= PARAM3= PARAM4= PARAM5= JDBC_CONNECTION_STRING= ERROR=1 WARN=2 INFO=3 DEBUG=4 LOGLEVEL=$WARN ###################################################################### # some functions SCRIPTNAME=$(basename $0) function usage() { echo >&2 "usage: $SCRIPTNAME -a appname {-e envname|-T template}" echo >&2 " $SCRIPTNAME -a appname -e envname" echo >&2 " create and start an application" echo >&2 " $SCRIPTNAME -a appname -T template" echo >&2 " create an application and a configuration template" echo >&2 "" echo >&2 " -a appname application name (required)" # "{-e envname|-T template} [-f appfile] [-k keyname] [-i image-id] [-t image-type] [-v version]" echo >&2 " -e environment Name of environment to create" echo >&2 " -T template Name of configuration template to create" echo >&2 " -d desciption application description" echo >&2 " -D desciption environment/template description" echo >&2 "" echo >&2 "* Instance parameters" echo >&2 " -i image-id AMI id" echo >&2 " -t instance-type Instance type, e.g. 'm1.small' or 't1.micro'" echo >&2 " -C solution-stack solution stack to use" echo >&2 " possible values:" echo >&2 " '32' -> 32bit Amazon Linux running Tomcat 6" echo >&2 " '64' -> 64bit Amazon Linux running Tomcat 6" echo >&2 " or any other solution stack as shown by" echo >&2 " elastic-beanstalk-list-available-solution-stacks" echo >&2 " -c CNAME CNAME to be used for the environment" echo >&2 " if empty, the environment is used" echo >&2 "" echo >&2 "* Security parameters" echo >&2 " -k keyname name of SSH access key configured in EC2" echo >&2 " -g security-group security group to apply" echo >&2 " -o optionsfile file containing additional options in JSON format" # application parameters echo >&2 "" echo >&2 "* Application parameters" echo >&2 " -V version label for application version" echo >&2 " -f location S3 location of application WAR file." echo >&2 " Format: 'bucket/file_key'" echo >&2 " If version is specified, but location is empty," echo >&2 " the AWS sample application is used" echo >&2 " -1 value PARAM1" echo >&2 " -2 value PARAM2" echo >&2 " -3 value PARAM3" echo >&2 " -4 value PARAM4" echo >&2 " -5 value PARAM5" echo >&2 " -j value JDBC_CONNECTION_STRING" # min, max instance count echo >&2 "" echo >&2 "* Load-balancing parameters" echo >&2 " -m max-count max number of instances to create" echo >&2 " -M min-count min number of instances to create" echo >&2 "" echo >&2 "* Other parameters" echo >&2 " -h usage show this help" echo >&2 " -v increase verbosity" echo >&2 " -q decrease verbosity" } function log() { local level=$1 shift if [ "$level" -le "$LOGLEVEL" ]; then echo >&2 "== " "$@" fi } function isLevel() { local level=$1 shift if [ $level -le $LOGLEVEL ]; then return 0 fi return 1 } function error() { log $ERROR "$@" } function isError() { isLevel $ERROR } function warn() { log $WARN "$@" } function isWarn() { isLevel $WARN } function info() { log $INFO "$@" } function isInfo() { isLevel $INFO } function debug() { log $DEBUG "$@" } function isDebug() { isLevel $DEBUG } function configOption() { local namespace=$1 local name=$2 local value=$3 echo "{\"Namespace\":\"$namespace\",\"OptionName\":\"$name\",\"Value\":\"$value\"}" } function configure() { local separator="," while getopts "n" OPT $@; do case "$OPT" in n) separator="" esac done # reset index, remove parsed args optind=$OPTIND unset OPTIND if [ "$optind" -gt 0 ]; then shift $((optind - 1)) fi local file=$1 local namespace=$2 local name=$3 local value=$4 if [ -n "$value" ]; then echo $(configOption "$namespace" "$name" "$value") $separator >> "$file" fi } ###################################################################### # parse options while getopts "a:e:f:d:D:i:T:k:t:hv:Vqo:1:2:3:4:5:j:g:c:C:m:M:" OPT $@; do case "$OPT" in h) usage; exit 0;; v) LOGLEVEL=$((LOGLEVEL + 1));; q) LOGLEVEL=$((LOGLEVEL - 1));; a) APP="$OPTARG";; f) APPFILE="$OPTARG";; # environment name e) ENV="$OPTARG";; d) APPDESCR="$OPTARG";; D) ENVDESCR="$OPTARG";; i) IMAGEID="$OPTARG";; t) INSTANCETYPE="$OPTARG";; V) VERSION="$OPTARG";; k) KEYNAME="$OPTARG";; T) TMPL="$OPTARG";; o) OPTFILE="$OPTARG";; # application parameters 1) PARAM1="$OPTARG";; 2) PARAM2="$OPTARG";; 3) PARAM3="$OPTARG";; 4) PARAM4="$OPTARG";; 5) PARAM5="$OPTARG";; j) JDBC_CONNECTION_STRING="$OPTARG";; g) SECGROUP="$OPTARG";; c) CNAME="$OPTARG";; # min, max instance count m) MAX_COUNT="$OPTARG";; M) MIN_COUNT="$OPTARG";; # container C) CONTAINER="$OPTARG";; esac done # reset index, remove parsed args optind=$OPTIND unset OPTIND if [ "$optind" -gt 0 ]; then shift $((optind - 1)) fi ###################################################################### # validate args # app name is required if [ -z "$APP" ]; then error "missing app name (paramater -a)" exit 10 fi if [ -z "$APPDESCR" ]; then APPDESCR="$APP" fi # environment and/or template is required if [ -z "$ENV" ]; then if [ -z "$TMPL" ]; then #error "missing environment or template name (paramater -e or -T)" #exit 10 TMPL="$DEFAULT_TEMPLATE_NAME" # default configuration name is "Default" fi else if [ -z "$TMPL" ]; then # use environment name as template name #TMPL="$ENV" TMPL="$DEFAULT_TEMPLATE_NAME" # default configuration name is "Default" fi if [ -z "$CNAME" ]; then CNAME="$ENV" fi fi if [ -z "$ENVDESCR" ]; then ENVDESCR="$ENV" fi if [ -z "$ENVDESCR" ]; then ENVDESCR="$TMPL" fi # version is optional if [ -z "$VERSION" ]; then #VERSION=initial VERSION=$(date +%Y%m%d-%H%M%S) fi # allow to choose between 32bit and 64 bit Tomcat container case "$CONTAINER" in 64) CONTAINER="64bit Amazon Linux running Tomcat 6";; 32) CONTAINER="32bit Amazon Linux running Tomcat 6";; esac ###################################################################### # perform actions # check name of keypair if [ -n "$KEYNAME" ]; then # check whether ec2-describe-keypairs is available if which ec2-describe-keypairs >/dev/null 2>&1 ; then if ec2-describe-keypairs "$KEYNAME" >/dev/null 2>&1; then debug "keypair $KEYNAME is valid" else error "keypair $KEYNAME does not exist" exit 10 fi fi fi # check whether application already exists appCheck=$(elastic-beanstalk-describe-applications -a "$APP" | grep -iv "No applications found") if [ -z "$appCheck" ]; then info "creating app $APP" elastic-beanstalk-create-application -a "$APP" -d "$APPDESCR" RC=$? debug "result of elastic-beanstalk-create-application: $RC" if [ "$RC" -ne 0 ]; then echo "failed to create application $APP" exit $RC fi else info "app $APP already exists" fi if [ -n "$APPFILE" -o -n "$VERSION" ]; then # check whether app version already exists: # list version (and remove headers and "no such version" warning) versions=$(elastic-beanstalk-describe-application-versions -a "$APP" -l "$VERSION" | grep -Ev "were found|----|DateCreated") if [ -z "$versions" ]; then info "creating app $APP version $VERSION from ${APPFILE:-AWS sample application}" # check whether APPFILE is a local file instead of S3 location if [ -f "$APPFILE" ]; then warn "$APPFILE is a local file, but a S3 location formatted as 'bucket/file_key' is expected!" fi elastic-beanstalk-create-application-version -a "$APP" -l "$VERSION" -s "$APPFILE" RC=$? debug "result of elastic-beanstalk-create-application-version: $RC" if [ "$RC" -ne 0 ]; then echo "failed to create application $APP version $VERSION from $APPFILE" exit $RC fi else # TODO update version if location differs info "app $APP version $VERSION already exists" fi fi # create temporary options file TMPOPTFILE=`mktemp -p "$TMPDIR" launcfg.XXXXXXX` debug "create temporary options file in $TMPOPTFILE" # open options array echo "[" > "$TMPOPTFILE" if [ -f "$OPTFILE" ]; then # add existing user options cat "$OPTFILE" >> "$TMPOPTFILE" fi configure "$TMPOPTFILE" "aws:autoscaling:launchconfiguration" "ImageId" "$IMAGEID" configure "$TMPOPTFILE" "aws:autoscaling:launchconfiguration" "SecurityGroups" "$SECGROUP" configure "$TMPOPTFILE" "aws:autoscaling:launchconfiguration" "InstanceType" "$INSTANCETYPE" configure "$TMPOPTFILE" "aws:autoscaling:launchconfiguration" "EC2KeyName" "$KEYNAME" configure "$TMPOPTFILE" "aws:elasticbeanstalk:application:environment" "PARAM1" "$PARAM1" configure "$TMPOPTFILE" "aws:elasticbeanstalk:application:environment" "PARAM2" "$PARAM2" configure "$TMPOPTFILE" "aws:elasticbeanstalk:application:environment" "PARAM3" "$PARAM3" configure "$TMPOPTFILE" "aws:elasticbeanstalk:application:environment" "PARAM4" "$PARAM4" configure "$TMPOPTFILE" "aws:elasticbeanstalk:application:environment" "PARAM5" "$PARAM5" configure "$TMPOPTFILE" "aws:elasticbeanstalk:application:environment" "JDBC_CONNECTION_STRING" "$JDBC_CONNECTION_STRING" configure "$TMPOPTFILE" "aws:autoscaling:asg" "MaxSize" "$MAX_COUNT" # add final param without trailing comma configure -n "$TMPOPTFILE" "aws:autoscaling:asg" "MinSize" "$MIN_COUNT" # close options array echo "]" >> "$TMPOPTFILE" # check whether app version already exists: # NOTE we abuse elastic-beanstalk-describe-configuration-settings to check, whether the configuration template exists templateCheck=$(elastic-beanstalk-describe-configuration-settings -a "$APP" -t "$TMPL"|head -1|grep -i "error") if [ -n "$templateCheck" ]; then info "creating configuration template $TMPL" # error, create configuration template if [ -n "$CONTAINER" ]; then elastic-beanstalk-create-configuration-template -a "$APP" -t "$TMPL" -d "$ENVDESCR" -f "$TMPOPTFILE" -s "$CONTAINER" else elastic-beanstalk-create-configuration-template -a "$APP" -t "$TMPL" -d "$ENVDESCR" -f "$TMPOPTFILE" fi else info "updating existing configuration template $TMPL" # configuration template already exists, update it if [ -n "$CONTAINER" ]; then warn "cannot update solution stack for existing configuration template ($CONTAINER)" fi elastic-beanstalk-update-configuration-template -a "$APP" -t "$TMPL" -d "$ENVDESCR" -f "$TMPOPTFILE" fi RC=$? debug "result of elastic-beanstalk-create-configuration-template: $RC" # delete temporary options file if [ -n "$TMPOPTFILE" -a -f "$TMPOPTFILE" ]; then if isDebug; then debug "options" cat "$TMPOPTFILE" fi rm -f "$TMPOPTFILE" fi if [ "$RC" -ne 0 ]; then error "failed to create configuration template" exit $RC fi if [ -n "$ENV" ]; then # check availablility of CNAME checkDNS=$(elastic-beanstalk-check-dns-availability -c "$CNAME" | tail -1 | grep -i "true") if [ -n "$checkDNS" ]; then info "creating environment $ENV for app $APP version $VERSION" elastic-beanstalk-create-environment -a "$APP" -l "$VERSION" -e "$ENV" -t "$TMPL" -c "$CNAME" -d "$ENVDESCR" else error "failed to create environment $ENV for app $APP version $VERSION: CNAME $CNAME is not available" fi fi exit $RC