#! /bin/bash

# This script enables a running instance to run RightScale's RightLink10 agent and to become
# a "RightScale server" that benefits from all the features of the RS platform. It does this
# in 3 steps: (1) locate a unique identifier about this instance (such as instance ID), (2)
# locate the RightScale ServerTemplate and Deployment to use, and (3) enable the instance into
# a RightScale server.
# This script is deliberately written in bash because (a) that works on all linux distros, and
# (b) almost everyone can figure out how to make modifications. For example, you may want to
# pass an href to the ServerTemplate instead of the name, or you may need to change how the
# instance self-identifies, or you may want to install RL10 differently, etc.
# The script is long in lines, but the sections to make RS API calls are pretty repetitive
# so once you grok one you don't need to read the others line-by-line to understand what's
# happening.
#
# For detailed instructions, see http://docs.rightscale.com/rl10/reference/rl10_enable_running_instances.html

# Check to make sure script is being run as root
if [[ `id -u` -ne 0 ]]; then
  echo "ERROR: The script must be run with root privileges." >&2
  exit 1
fi

# Create a log file
echo "Logging to /var/log/rightlink.log" 1>&2
exec > >(tee -a /var/log/rightlink.log)
exec 2>&1
touch /var/log/rightlink.log && chmod 600 /var/log/rightlink.log
# Sleep command added on exit ensure output to stdout finishes before returning to command prompt
trap "sleep 1" EXIT

echo "`date` Starting Enablement"

# ===== Default configuration

URL="https://rightlink.rightscale.com/rll/10.6.2/rightlink.tgz"
DEFAULT_SERVER="my.rightscale.com"
DEFAULT_NAME="RightLink Enabled #$$"
DEFAULT_INSTANCE_TYPE="auto"

# Determine if /usr/local/bin is read-only. If so, use /opt/bin to install executables
mkdir -p /usr/local/bin || true
[ -w /usr/local/bin ] && BIN_DIR=/usr/local/bin || BIN_DIR=/opt/bin

if [[ ! "$PATH" =~ $BIN_DIR ]]; then
  export PATH="$PATH:$BIN_DIR"
fi

show_help() {
  cat <<EOT
Usage: ${0##*/} [-options...]
Enable an instance to become a full RightScale server to get monitoring and operational scripts.
  -k refresh_token      RightScale API refresh token from the dash Settings>API Credentials (req.)
  -d deployment_name    the name of the pre-existing deployment to put the server
  -e deployment_href    HREF of the deployment to put the server. alternate to the name of
                        the deployment (ex. /api/deployments/123456789)
  -t st_name            the name of the ServerTemplate to use.
  -r st_href            HREF of the ServerTemplate to associate with this instance. alternate to
                        the name of the ServerTemplate (ex. /api/server_templates/123456789)
  -n server_name        the name to call the server, default is current instance name or $DEFAULT_NAME
  -p inputs             the server inputs in the form of NAME=key:value, use multiple flags for
                        multiple inputs
  -c cloud_type         the cloud type the instance is in. used for self-detection
                        supported values: amazon, azure, cloud_stack, google, open_stack_v2, open_stack_v3,
                        rackspace_next_gen, soft_layer, vscale, uca
  -f cloud_name         the name of the cloud the instance is located
  -i instance_href      this instance's HREF (disables self-detection)
                        (ex. /api/clouds/1/instances/123456ABCDEF)
  -s instance_type      the name of the instances type (for UCA clouds only), default: $DEFAULT_INSTANCE_TYPE
  -a api_hostname       the hostname for the RightScale API, default: $DEFAULT_SERVER
  -x proxy_url          have RightLink use HTTP proxy. Will also install RightLink through proxy
  -y no_proxy           a list of hosts to not proxy. List is inherited by scripts/recipes as an environment variable
  -z                    ignore check for cloud-init in order to support snapshotting for custom image
  -h                    show this help

Required Inputs: -k refresh_token,
                 -t st_name or -r st_href,
                 -d deployment_name or -e deployment_href,
                 -c cloud_type or -i instance_href or -f cloud_name
EOT
}

if ! which curl >/dev/null; then
  echo "ERROR: Cannot find curl: it is required for this script." >&2
  exit 1
fi

if ! which tar >/dev/null; then
  echo "ERROR: Cannot find tar: it is required for this script." >&2
  exit 1
fi

if ! which sudo >/dev/null; then
  echo "ERROR: Cannot find sudo: it is required for RightLink." >&2
  exit 1
fi

# ===== Check for previous versions of RightLink and warn user to uninstall

if dpkg --get-selections 2>/dev/null | grep -E "^(rightlink6|rightlink5|rightscale)\s" >/dev/null || \
  rpm -qa --queryformat '%{Name}\n' 2>/dev/null | grep -E "^(rightlink6|rightlink5|rightscale)$" >/dev/null; then
  echo "ERROR: Prior RightLink version found. Enabling this instance will install RightLink 10, our latest agent." >&2
  echo "RightLink 6 or any previous version must be uninstalled before proceeding." >&2
  echo "Please visit https://support.rightscale.com/12-Guides/RightLink_6/Uninstall_RightLink for uninstall instructions." >&2
  exit 1
fi

# ===== Parse arguments

refresh_token=
deployment_name=
deployment_href=
st_name=
st_href=
server_name=
inputs=()
cloud_type=
cloud_name=
instance_href=
api_hostname=
cloud_init_check=1
not_root=
instance_type=

while getopts "hvlzk:d:e:t:r:n:p:c:f:i:a:x:y:s:" opt; do
  case "$opt" in
    h) show_help; exit 0 ;;
    v) set -x ;;
    k) refresh_token="$OPTARG" ;;
    d) deployment_name="$OPTARG" ;;
    e) deployment_href="$OPTARG" ;;
    t) st_name="$OPTARG" ;;
    r) st_href="$OPTARG" ;;
    n) server_name="$OPTARG" ;;
    p) inputs+=("$OPTARG") ;;
    c) cloud_type="$OPTARG" ;;
    f) cloud_name="$OPTARG" ;;
    i) instance_href="$OPTARG" ;;
    s) instance_type="$OPTARG" ;;
    a) api_hostname="$OPTARG" ;;
    x) http_proxy="$OPTARG" ;;
    y) no_proxy="$OPTARG" ;;
    z) cloud_init_check=0 ;;
    '?') show_help >&2; exit 1 ;;
  esac
done

# Shift off the options and optional --.
shift "$((OPTIND-1))"

if [[ -z "$refresh_token" ]]; then
  echo "ERROR: -k refresh_token missing." >&2
  show_help >&2
  exit 1
fi

if [[ -z "$st_name" && -z "$st_href" ]]; then
  echo "ERROR: -t st_name and -r st_href missing. At least one must be present." >&2
  show_help >&2
  exit 1
fi

if [[ -z "$instance_href" && -z "$cloud_type" && -z "$cloud_name" ]]; then
  echo "ERROR: -c cloud_type and -i instance_href and -f cloud_name missing. At least one must be present." >&2
  show_help >&2
  exit 1
fi

if [[ -z "$api_hostname" ]]; then
  if [[ -n "$DEFAULT_SERVER" ]]; then
    api_hostname="$DEFAULT_SERVER"
    echo "Using API Host $api_hostname"
  else
    echo "ERROR: -a api_hostname missing." >&2
    show_help >&2
    exit 1
  fi
fi

if [[ -n "$http_proxy" ]]; then
  export http_proxy="$http_proxy"
  export https_proxy="$http_proxy"
  echo "RightLink client will be proxied through: $http_proxy"
fi

if [[ -z "$deployment_name" && -z "$deployment_href" ]]; then
  echo "ERROR: -d deployment_name and -e deployment_href missing. At least one must be present." >&2
  show_help >&2
  exit 1
fi

# ===== Download and expand RightLink
echo "Downloading tarball from $URL, extracting into /tmp/rightlink"
cd /tmp
rm -rf rightlink rightlink.tgz
for s in 2 4 6 8 12 16 20 24 32 64; do
  if curl -s -o rightlink.tgz $URL ; then
    break
  fi
  echo "`date` Download error, sleeping $s seconds"
  sleep "$s"
done
# Expand the archive into /tmp/rightlink
tar zxf rightlink.tgz
cd rightlink

# ===== Install RSC command line tool

# Where to put the executable
RSC=$BIN_DIR/rsc

# Put the binary into a bin directory
mkdir -p ${RSC%/*}
cp rsc $RSC
chmod +x $RSC

# ===== Retrieve cloud metadata and identify the instance in RightScale

# Ideally we identify the instance by it's cloud id, known as resource_uid in RS, but sometimes
# the cloud doesn't expose that to the instance, so we have to revert to something else, such
# as the instance's public or private IP.
resource_label="resource_uid"

# Query API based on supplied Instance HREF to ensure that the instance running the enable script
# is the same as the instance referenced in the Instance HREF
if [[ -n "$instance_href" ]]; then
  echo -n "Finding instance $instance_href ... "
  json=$(rsc --key "$refresh_token" --host "$api_hostname" cm15 show "$instance_href")
  resource_uid=$(echo "$json" | rsc --x1 '.resource_uid' json 2>/dev/null)
  declare -a "private_ip_address=($(echo "$json" | rsc --xm '.private_ip_addresses string' json 2>/dev/null))"
  declare -a "public_ip_address=($(echo "$json" | rsc --xm '.public_ip_addresses string' json 2>/dev/null))"
  if [[ -n "$resource_uid" ]]; then
    echo "Instance found with id: $resource_uid"
  else
    echo "ERROR: Failed to retrieve resource_uid. Use -c cloud_type instead of -i instance_href for self-detection." >&2
    exit 1
  fi
  cloud_href=$(echo "$json" | rsc --x1 ':has(.rel:val("cloud")).href' json 2>/dev/null)
  if [[ -z "$cloud_href" ]]; then
    echo "ERROR: Failed to retrieve cloud_href. Use -c cloud_type instead of -i instance_href for self-detection." >&2
    exit 1
  fi

  echo -n "Finding cloud type for $resource_uid ... "
  existing_cloud_type=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 '.cloud_type' cm15 show "$cloud_href" 2>/dev/null)
  if [[ -n "$existing_cloud_type" ]]; then
    if [[ -n "$cloud_type" && "$cloud_type" != "$existing_cloud_type" ]]; then
      echo "ERROR: -c cloud_type input does not match instance cloud type ($cloud_type != $existing_cloud_type)" >&2
      exit 1
    else
      cloud_type="$existing_cloud_type"
      echo "cloud found with type: $cloud_type"
    fi
  elif [[ -z "$cloud_type" ]]; then
    echo "ERROR: Failed to retrieve cloud type. Use -c cloud_type instead of -i instance_href for self-detection." >&2
    exit 1
  fi
fi

# Identify the cloud HREF from the cloud name if it is provided.
if [[ -n "$cloud_name" ]]; then
  echo -n "Finding cloud $cloud_name ... "
  json=$(rsc --key "$refresh_token" --host "$api_hostname" cm15 index clouds "filter[]=name==$cloud_name")
  cloud_href=$(echo "$json" | rsc --x1 ':has(.rel:val("self")).href' json 2>/dev/null)
  if [[ -z "$cloud_href" ]]; then
    echo "ERROR: Failed to find cloud. Use -c cloud_type instead of -f cloud_name for self-detection." >&2
    exit 1
  fi
  cloud_type=$(echo "$json" | rsc --x1 '.cloud_type' json 2>/dev/null)
  echo "cloud_href=$cloud_href"
fi

# Identify instance_id through the metadata to either determine the instance HREF or to compare
# against the resource_uid of the user supplied instance HREF
if [[ -n "$cloud_type" ]]; then
  case "$cloud_type" in

  amazon)
    echo -n "Retrieving EC2 metadata ... "
    instance_id=`curl --noproxy '*' -s --retry 3 http://169.254.169.254/latest/meta-data/instance-id`
    ;;

  azure)
    echo -n "Retrieving Azure metadata ... "
    re="<(ns1:)?HostName>(.*)<\/(ns1:)?HostName>"
    if [[ `cat /var/lib/waagent/ovf-env.xml` =~ $re ]]; then
      instance_id="${BASH_REMATCH[2]}"
    fi
    ;;

  azure_v2)
    echo -n "Retrieving Azure Resource Manager uuid using waagent config file ... "
    re="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}.status"
    file=`ls -tr /var/lib/waagent/ExtensionsConfig.*.xml 2> /dev/null | tail -n 1`
    if [[ ! -e $file ]]; then
      echo "ERROR: Azure ExtensionsConfig xml file not found"
      exit 1
    fi
    if [[ `cat $file` =~ $re ]]; then
      instance_id=`echo ${BASH_REMATCH[0]} | sed s/.status//g`
    fi
    ;;

  cloud_stack)
    echo -n "Retrieving Cloudstack metadata ... "
    directories=(dhclient dhcp dhcp3 dhcpcd)
    for dir in "${directories[@]}"; do
      if [[ -d /var/lib/$dir ]]; then
        directory="$dir"
      fi
    done
    for file in `ls /var/lib/$directory/ -t`; do
      re=".*\.lease"
      if [[ "$file" =~ $re ]]; then
        lease_file="/var/lib/$directory/$file"
        break
      fi
    done
    if [[ -n "$lease_file" ]]; then
      re="dhcp-server-identifier.([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})"
      while read line; do
        if [[ "$line" =~ $re ]]; then
          dhcp="${BASH_REMATCH[1]}"
        fi
      done < $lease_file
    fi
    # Location and format of lease file is different for CoreOS
    if [[ -z "$dhcp" ]]; then
      for file in `ls /run/systemd/netif/leases/`; do
        re="SERVER_ADDRESS=([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})"
        while read line; do
          if [[ "$line" =~ $re ]]; then
            dhcp="${BASH_REMATCH[1]}"
          fi
        done < /run/systemd/netif/leases/$file
      done
    fi
    if [[ -z "$dhcp" ]]; then
      echo "ERROR: Could not locate metadata server address." >&2
      exit 1
    fi
    instance_id=`curl --noproxy '*' http://$dhcp/latest/meta-data/instance-id`
    ;;

  google)
    echo -n "Retrieving Google metadata ... "
    hostname=`curl --noproxy '*' http://metadata.google.internal/computeMetadata/v1/instance/hostname \
             -H "Metadata-Flavor: Google" | sed -Ee 's/\..*//g' | xargs echo -n`
    project=`curl --noproxy '*' http://metadata.google.internal/computeMetadata/v1/project/project-id \
            -H "Metadata-Flavor: Google"`
    instance_id="projects/${project}/instances/${hostname}"
    ;;

  open_stack_v2|open_stack_v3)
    echo -n "Retrieving Openstack metadata ... "
    instance_id=`curl --noproxy '*' http://169.254.169.254/openstack/latest/meta_data.json | \
                grep -o '"uuid": "[0-9a-h\-]*"' | \
                sed -e s/\"//g | sed -e s/uuid:\ //g | xargs echo -n`
    ;;

  rackspace_next_gen)
    echo -n "Retrieving Rackspace metadata ... "
    instance_id=`xenstore read name | sed 's/instance-//' | xargs echo -n`
    ;;

  soft_layer)
    echo -n "Retrieving SoftLayer metadata ... "
    instance_id=`curl --noproxy '*' -s --retry 3 \
                https://api.service.softlayer.com/rest/v3.1/SoftLayer_Resource_Metadata/Id.txt | \
                xargs echo -n`
    ;;

  vscale)
    # For vCenter there is no standard metadata, so we have to have a couple of options and
    # fall-back to IP addresses
    if which vmtoolsd >/dev/null; then
      echo -n "Retrieving vScale metadata ... "
      metadata=`vmtoolsd --cmd 'info-get guestinfo.metadata'`
      ovfEnv=`vmtoolsd --cmd 'info-get guestinfo.ovfEnv'`
      if [[ -n "$metadata" ]]; then
        instance_id=`echo $metadata | grep -o 'vs_instance_id=vm-[0-9]*' | \
                    sed 's/vs_instance_id=//' | xargs echo -n`
      elif [[ -n "$ovfEnv" ]]; then
        instance_id=`echo $ovfEnv | grep -o 'vCenterId="vm-[0-9]*"' | sed 's/vCenterId=//' | xargs echo -n`
      fi
    fi
    if [[ -z "$instance_id" ]]; then
      echo "Unable to retrieve metadata using VMWare Tools."
      echo -n "Trying to match instance using IP address ... "
      interface=`ip addr | awk '/^[0-9]/{ gsub(":", "", $2); print $2 }' | grep -v lo`
      int_count=0
      for interf in ${interface[@]}; do
        instance_id[$int_count]=`ip addr show dev $interf | \
                   grep -Eo 'inet [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | \
                   sed -Ee 's/inet\ //g' | xargs echo -n`
        (( int_count = int_count + 1 )) ;
      done

      if echo $instance_id | grep -q -E '^10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.'; then
        echo "Warning: In certain RCA-V networking configurations (i.e. using multiple subnets), using private IP" \
          "as a unique identifier may cause problems.  Only use this method if you are sure you do not have any" \
          "overlapping IP space in any RCA-V clouds."
        resource_label="private_ip_address"
      else
        resource_label="public_ip_address"
      fi
    fi
    ;;

  uca) # RightScale Universal Cloud Appliance
    # Find UCA cloud if needed
    if [[ -z "$cloud_href" ]]; then
      echo -n "Locating UCA cloud ... "
      cloud_href=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 'object:has(.rel:val("self")).href' \
	               cm15 index clouds 'filter[]=cloud_type==uca')
      if [[ -z "$cloud_href" ]]; then
        echo "ERROR: Failed to find cloud." >&2
        exit 1
      fi
      echo "cloud_href=$cloud_href"
    fi

    echo -n "Locating UCA datacenter ... "
    datacenter_href=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 'object:has(.rel:val("self")).href' \
	                  cm15 index $cloud_href/datacenters)
    if [[ -z "$datacenter_href" ]]; then
      echo "ERROR: Failed to find datacenter." >&2
      exit 1
    fi
    echo "datacenter_href=$datacenter_href"

    echo -n "Locating UCA generic image ... "
    image_href=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 'object:has(.rel:val("self")).href' \
               cm15 index $cloud_href/images 'filter[]=name==generic')
    if [[ -z "$image_href" ]]; then
      echo "ERROR: Failed to find generic image." >&2
      exit 1
    fi
    echo "image_href=$image_href"

    if [[ -z "$instance_type" ]]; then
      instance_type="$DEFAULT_INSTANCE_TYPE"
    fi
    echo -n "Locating UCA instance type '$instance_type' ... "
    instance_type_href=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 'object:has(.rel:val("self")).href' \
	                     cm15 index $cloud_href/instance_types "filter[]=resource_uid==$instance_type")
    if [[ -z "$instance_type_href" ]]; then
      echo "ERROR: Failed to find instance type." >&2
      exit 1
    fi
    echo "instance_type_href=$instance_type_href"
    ;;

  *)
    echo "ERROR: $cloud_type is an unsupported cloud." >&2
    show_help >&2
    exit 1
    ;;

  esac

  if [[ "$cloud_type" != "uca" ]]; then
    if [[ -z "$instance_id" ]]; then
      echo "ERROR: Failed to find instance_id." >&2
      exit 1
    else
      echo "instance_id=$instance_id"
    fi
  fi
else
  echo "ERROR: cloud_type could not be determined." >&2
  exit 1
fi

# ===== Check for cloud-init to create dependency if a snapshot will be made from the enabled instance

# Cloud-init is no longer required for stop/start as the rightscale identity file can be reused.
# If the instance does not have the correct version of cloud-init installed and the override flag
# has been passed, the init scripts are updated so that rightlink no longer requires cloud-init to start.
# However, we leave the dependency on cloud-init to support creating a snapshot of the image
# after enablement is complete.

if [[ "$cloud_init_check" -eq 1 && "$cloud_type" != "uca" ]]; then
  echo -n "Checking for cloud-init to support image snapshot ... "
  case "$cloud_type" in

  soft_layer)
    if dpkg-query -W -f='${Version}.${Maintainer}' cloud-init 2>/dev/null | grep -i "rightscale" >/dev/null || \
      rpm --query --qf '%{Version}.%{Release}.%{Vendor}' cloud-init 2>/dev/null | grep -i "rightscale" >/dev/null; then
      echo "cloud-init found."
    else
      echo "ERROR: Cannot find RightScale cloud-init: it is required to support image snapshot." >&2
      echo "Please visit http://docs.rightscale.com/rl10/reference/rl10_cloud_init_installation.html" >&2
      echo "for install instructions or use the -z flag to override this check if this server" >&2
      echo "will not be used to create an image snapshot." >&2
      exit 1
    fi
    ;;

  *)
    if which cloud-init >/dev/null || which coreos-cloudinit >/dev/null; then
      echo "cloud-init found."
    else
      echo "ERROR: Cannot find cloud-init: it is required to support image snapshot." >&2
      echo "Please visit http://docs.rightscale.com/rl10/reference/rl10_cloud_init_installation.html" >&2
      echo "for install instructions or use the -z flag to override this check if this server" >&2
      echo "will not be used to create an image snapshot." >&2
      exit 1
    fi
    ;;

  esac
else
  sed -i -e 's/ and stopped cloud-final//' rightlink.conf 2>/dev/null
  sed -i -e 's/ cloud-final.service//' rightlink.service 2>/dev/null
  sed -i -e 's/ cloud-final//' rightlink.init 2>/dev/null
fi

# ===== Check instance metadata against the metadata of the instance HREF input

# We don't have an instance yet in the UCA case, so there's nothing to check here
if [[ "$cloud_type" != "uca" ]]; then
  if [[ -n "$instance_href" ]]; then
    # Compare resource_uid of instance href against the current instance's metadata
    echo -n "Comparing current instance_id against instance_href instance_id ... "
    if [[ "$resource_label" == "private_ip_address" ]]; then
      ip_found=''
      for inst_ip in ${instance_id[@]}; do
        if [[ "$inst_ip" != "$private_ip_address" ]]; then
          echo "Instance private_ip_address ($instance_id) does not match instance_href private_ip_address ($private_ip_address)" >&2
        else
          ip_found="true"
        fi ;
      done
      if [[ $ip_found != "true" ]]; then
        echo "ERROR: Instance private_ip_addresses do no match instance_href private_ip_addresses"
        exit 1
      fi
    elif [[ "$resource_label" == "public_ip_address" ]]; then
      ip_found=''
      for inst_ip in ${instance_id[@]}; do
        if [[ "$instance_id" != "$public_ip_address" ]]; then
          echo "Instance public_ip_address ($instance_id) does not match instance_href public_ip_address ($public_ip_address)" >&2
        else
          ip_found="true"
        fi ;
      done
        if [[ $ip_found != "true" ]]; then
          echo "ERROR: Instance private_ip_addresses do no match instance_href private_ip_addresses"
          exit 1
        fi
    else
      if [[ "$instance_id" != "$resource_uid" ]]; then
        echo "ERROR: Instance cloud metadata resource_uid ($instance_id) does not match instance_href resource_uid ($resource_uid)" >&2
        exit 1
      fi
    fi
    echo "instance_ids match."
  else
    # ===== Enumerate the clouds of the type we're looking for if instance HREF was not supplied
    echo -n "Enumerating clouds of type $cloud_type ... "
    declare -a "cloud_hrefs=($(rsc --key "$refresh_token" --host "$api_hostname" --xm ':has(.rel:val("self")).href' \
                cm15 index /api/clouds "filter[]=cloud_type==$cloud_type"))"
    if [[ "${#cloud_hrefs[@]}" -eq 0 ]]; then
      echo "ERROR: No cloud of type $cloud_type found in account." >&2
      exit 1
    else
      echo "Found ${#cloud_hrefs[@]} clouds for cloud type $cloud_type."
    fi

    # ===== Now locate the instance in one of the clouds

    # We try a bunch of times to locate this instance in the cloud, the reason is that it may
    # take a while for the platform to discover the instance if it just got launched.
    # Wait up to 60 minutes for the instance to be discovered by the RightScale platform
    for try in $(seq 1 60); do
      # Iterate through all clouds of the type we're looking in
      for cloud_href in "${cloud_hrefs[@]}"; do
        echo -n "Finding instance $instance_id in $cloud_href ... "
        if [[ "$resource_label" == *"ip_address" ]]; then
          for instance in `rsc --key "$refresh_token" --host "$api_hostname" --xm ':has(.rel:val("self")).href' \
                        cm15 index "$cloud_href/instances" "filter[]=$resource_label==$instance_id" \
                        "filter[]=state<>terminated" "filter[]=state<>decommissioning" "filter[]=state<>terminating" \
                        "filter[]=state<>stopping" "filter[]=state<>provisioned" "filter[]=state<>failed" 2>/dev/null \
                        | sed s/\"//g`; do
            check_ip=`rsc --key "$refresh_token" --host "$api_hostname" --x1 ".${resource_label}es" cm15 show "${instance}" \
                    | sed 's/\[\|\"\|\]//g'`
            if [[ "${instance_id}" == "${check_ip}" ]]; then
              instance_href=${instance}
            fi
          done
        else
          instance_href=`rsc --key "$refresh_token" --host "$api_hostname" --x1 ':has(.rel:val("self")).href' \
                        cm15 index "$cloud_href/instances" "filter[]=$resource_label==$instance_id" \
                        "filter[]=state<>terminated" "filter[]=state<>decommissioning" "filter[]=state<>terminating" \
                        "filter[]=state<>stopping" "filter[]=state<>provisioned" "filter[]=state<>failed" 2>/dev/null`
        fi
        if [[ -n "$instance_href" ]]; then
          echo "instance_href=$instance_href"
          break
        fi
        echo "none found."
      done
      if [[ -n "$instance_href" ]]; then
        break
      fi
      if [[ "$try" -ne 60 ]]; then
        echo "Attempt $try unsuccessful, sleeping 60 seconds and retrying ... "
        sleep 60
      fi
    done
    if [[ -z "$instance_href" ]]; then
      echo "ERROR: Cannot find instance." >&2
      exit 1
    fi
  fi
fi

if [[ -z "$server_name" ]]; then
  server_name=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 ':root > .name' cm15 show "$instance_href" 2>/dev/null)
  if [[ -n "$server_name" ]]; then
    echo "Instance name used to set server name: $server_name"
  else
    if [[ -n "$DEFAULT_NAME" ]]; then
      server_name="$DEFAULT_NAME"
      echo "Default name used to set server name: $server_name"
    else
      echo "ERROR: -n server_name missing." >&2
      show_help >&2
      exit 1
    fi
  fi
fi

# ===== Find Server Template
# We locate the desired ServerTemplate based on the command line argument. The ST is required
# to create a server. It will provide boot scripts, operational scripts, decommissioning scripts,
# alert definitions to the existing instance. An empty ST with an MCI to launch the surrogate
# is the minimum required.

if [[ -n "$st_href" ]]; then
  echo -n "Finding ServerTemplate '$st_href' ... "
  st_href_check=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 ':has(.rel:val("self")).href' cm15 show "$st_href" 2>/dev/null)
  if [[ -z "$st_href_check" ]]; then
    echo "ERROR: Could not find ServerTemplate with HREF $st_href" >&2
    exit 1
  fi
else
  echo -n "Finding ServerTemplate '$st_name' ... "
  # check for multiple STs by checking for multiple lineages
  # if a single lineage is found, check for multiple revisions
  json=$(rsc --key "$refresh_token" --host "$api_hostname" --xj ":has(.name:val(\"$st_name\"))" \
       cm15 index "/api/server_templates" "filter[]=name==$st_name")
  if [[ -z "$json" || "$json" == "[]" ]]; then
    echo "ERROR: Failed to find ServerTemplate. Please use -r ServerTemplate HREF." >&2
    exit 1
  fi
  declare -a "lineages=($(echo "$json" | rsc --xm '.lineage' json))"
  for l in "${lineages[@]}"; do
    if [[ -z "$lineage" ]]; then
      lineage="$l"
    elif [[ "$l" != "$lineage" ]]; then
      echo "ERROR: Multiple ServerTemplates found. Please use -r ServerTemplate HREF." >&2
      exit 1
    fi
  done
  declare -a "revisions=($(echo "$json" | rsc --xm '.revision' json))"
  declare -a "st_hrefs=($(echo "$json" | rsc --xm ':has(.rel:val("self")).href' json))"
  if [[ "${#revisions[@]}" -gt 1 ]]; then
    echo "ERROR: Multiple revisions found for $st_name. Please use -r ServerTemplate HREF for the desired revision." >&2
    # print out all the revisions and their hrefs if more than one were found
    for i in "${!revisions[@]}"; do
      echo "Revision: ${revisions[$i]} - HREF: ${st_hrefs[$i]}"
    done
    exit 1
  elif [[ "${#revisions[@]}" -eq 0 ]]; then
    echo "ERROR: Failed to find revisions for $st_name. Please use -r ServerTemplate HREF." >&2
    exit 1
  fi
  st_href="${st_hrefs[0]}"
fi
echo "st_href=$st_href"

# ===== Find Deployment
# We locate the desired deployment based on the command line argument. Alternatively, this
# could be changed to create a deployment.

if [[ -n "$deployment_href" ]]; then
  echo -n "Finding Deployment '$deployment_href' ... "
  deployment_href_check=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 ':has(.rel:val("self")).href' \
                        cm15 show "$deployment_href" 2>/dev/null)
  if [[ -z "$deployment_href_check" ]]; then
    echo "ERROR: Could not find Deployment with HREF $deployment_href" >&2
    exit 1
  fi
else
  echo -n "Finding Deployment '$deployment_name' ... "
  json=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 ":has(.name:val(\"$deployment_name\"))" \
       cm15 index "/api/deployments" "filter[]=name==$deployment_name" 2>/dev/null)
  if [[ -z "$json" ]]; then
    echo "ERROR: Failed to find deployment. Please use -e Deployment HREF." >&2
    exit 1
  fi
  deployment_href=$(echo "$json" | rsc --x1 ":has(.rel:val(\"self\")).href" json 2>/dev/null)
fi
echo "deployment_href=$deployment_href"

# ===== Convert inputs to rsc parameters
input_params=()
for input in "${inputs[@]}"; do
  re='^([^=]*)=(.*)$'
  if [[ "$input" =~ $re ]]; then
    input_params+=("server[instance][inputs][${BASH_REMATCH[1]}]=${BASH_REMATCH[2]}")
  else
    echo "ERROR: Malformed input '$input'. Please use the format NAME=key:value." >&2
    exit 1
  fi
done

# ===== UCA case: we're "launching" the server

if [[ "$cloud_type" == "uca" ]]; then
  echo -n "Checking to see if instance is already UCA enabled ... "
  # Use the RL10 mode of rsc to determine if this is an already enabled UCA instance because the instance will have
  # RightLink running and we'll be able to get information through its proxy if it is actually enabled
  json=$(rsc --rl10 cm15 index_instance_session /api/sessions/instance 2>/dev/null)
  if [[ $? -eq 0 ]]; then
    instance_href=$(rsc json --x1 ':has(.rel:val("self")).href' <<<$json)
    server_href=$(rsc json --x1 ':has(.rel:val("parent")).href' <<<$json)
    echo "instance_href=$instance_href, server_href=$server_href"
  else
    echo "Instance not enabled."
    echo -n "Creating UCA server ... "
    server_href=$(rsc --key "$refresh_token" --host "$api_hostname" \
                --xh 'location' cm15 create /api/servers \
                "server[name]=$server_name" \
                "server[deployment_href]=$deployment_href" \
                "server[instance][server_template_href]=$st_href" \
                "server[instance][cloud_href]=$cloud_href" \
                "server[instance][datacenter_href]=$datacenter_href" \
                "server[instance][image_href]=$image_href" \
                "server[instance][instance_type_href]=$instance_type_href" \
                "server[instance][cloud_specific_attributes][num_cores]=2" \
                "server[instance][cloud_specific_attributes][memory_mb]=2048" \
                "server[instance][cloud_specific_attributes][disk_gb]=1024" \
                "${input_params[@]}")
    if [[ -n "$server_href" ]]; then
      echo "Created server: server_href=$server_href"
    else
      echo "ERROR: Failed to create server." >&2
      exit 1
    fi

    echo -n "Launching UCA server ... "
    # If launch command is sent too fast after creating it, it will return a 422 unprocessable entity
    # Try to launch 5 times before failing
    for try in $(seq 1 5); do
      instance_href=$(rsc --key "$refresh_token" --host "$api_hostname" --xh 'location' cm15 launch $server_href 2>/dev/null)
      if [[ -n "$instance_href" ]]; then
        break
      fi
      sleep 1
    done
    if [[ -n "$instance_href" ]]; then
      echo "instance_href=$instance_href"
    else
      # If server isn't launchable we destroy it because it will get stuck in booting with no user-data
      rsc --key "$refresh_token" --host "$api_hostname" cm15 destroy "$server_href"
      echo "ERROR: Failed to launch server." >&2
      exit 1
    fi
  fi
else
# ===== Non-UCA case: we need to enable (wrap) the instance

  # ===== Retrieve instance data
  # Make sure that the server is in a valid state to be enabled
  # Wait up to 60 minutes for the server to get into a valid state
  for try in $(seq 1 60); do
    echo -n "Finding current instance ... "
    json=$(rsc --key "$refresh_token" --host "$api_hostname" cm15 show "$instance_href")
    state=$(echo "$json" | rsc --x1 '.state' json 2>/dev/null)
    if [[ "$state" != "running" && "$state" != "operational" ]]; then
      echo "Instance found, but in the wrong state: $state."
      if [[ "$try" -ne 60 ]]; then
      	echo "Attempt $try unsuccessful, sleeping 60 seconds and retrying ... "
      	sleep 60
      else
      	echo "ERROR: Instance failed to achieve running or operational state." >&2
      	exit 1
      fi
    else
      echo "Instance found and is in state: $state."
      break
    fi
  done

  # ===== Existing enable (wrapped) check

  echo -n "Checking to see if instance is already enabled ... "
  server_href=$(echo "$json" | rsc --x1 ':has(.rel:val("parent")).href' json 2>/dev/null)
  if [[ -n "$server_href" ]]; then
    echo "server_href=$server_href"
  else
    echo "Instance not enabled."

    # ===== Enable (wrap) the instance
    # We turn the instance into a server using the wrap_instance API call. The server will have
    # this instance as current instance and an appropriate next-instance. We pass the ServerTemplate
    # href in the call and this will determine the operational scripts available as well as any
    # alerts.

    echo -n "Enabling instance '$server_name' ... "
    server_href=$(rsc --key "$refresh_token" --host "$api_hostname" \
                --xh 'location' cm15 wrap_instance /api/servers \
                "server[name]=$server_name" \
                "server[deployment_href]=$deployment_href" \
                "server[instance][href]=$instance_href" \
                "server[instance][server_template_href]=$st_href" \
                "${input_params[@]}")
    if [[ -n "$server_href" ]]; then
      echo "Created server: server_href=$server_href"
    else
      echo "ERROR: Failed to create server." >&2
      exit 1
    fi
  fi
fi

# ===== Grab user-data from server
# We grab the user-data which is required for RightLink to be able to connect with the RS platform.
# The user-data is only part of the extended instance view

echo -n "Fetching user-data for instance '$instance_href' ... "
user_data=$(rsc --key "$refresh_token" --host "$api_hostname" --x1 '.user_data' cm15 show "$instance_href" "view=extended" 2>/dev/null)
if [[ -n "$user_data" ]]; then
  echo "user-data found."
  echo -e "$user_data\n" | sed -e 's/^/>> /'
else
  echo "ERROR: Failed to retrieve user-data." >&2
  exit 1
fi

# ===== Save user-data in a file
# We save the user-data in a file so it remains available if RightLink10 is restarted,
# for example due to a reboot. Here we handle query-string user-data format, which we may have
# to switch to mime depending on the MCIs we want to use and to support relaunching the
# instance.

ID=/var/lib/rightscale-identity
if [[ "$user_data" =~ client_secret= ]]; then
  # RL10 version of user-data
  rm -f $ID
  for k in account client_secret client_id api_hostname; do
    re="$k='([^']*)'"
    if [[ "$user_data" =~ $re ]]; then
      echo "$k=${BASH_REMATCH[1]}" >>$ID
    else
      echo "ERROR: Cannot find $k in user-data." >&2
      echo "User-data is '$user_data'"
      exit 1
    fi
  done
elif [[ "$user_data" =~ RS_rn_auth= ]]; then
  # RL6 version of user-data
  for k in RS_account RS_rn_id RS_rn_auth RS_server; do
    re="$k=([^&]*)"
    if [[ "$user_data" =~ $re ]]; then
      declare $k="${BASH_REMATCH[1]}"
    else
      echo "ERROR: Cannot find $k in user-data." >&2
      echo "User-data is '$user_data'"
      exit 1
    fi
  done
  # Remap to what's expected by RL10
  rm -f $ID
  echo "client_secret=$RS_rn_auth" >>$ID
  echo "client_id=$RS_rn_id" >>$ID
  echo "api_hostname=$RS_server" >>$ID
  echo "account=$RS_account" >>$ID
else
  echo "Cannot find the required RightScale info in the user-data produced for the server."
  echo "-> $user_data"
  echo "Is the default MCI of the ServerTemplate perhaps missing the required"
  echo "rs_agent:type=right_link_lite tag? (You can re-run this enable script with the -e"
  echo "flag after fixing the tag.)"
  exit 1
fi

# Workaround: we can't set the tag on instances and have the core recognize
# it, so we write it ourselves into the identity file. We also set the tag on the server
# Setting the server tags ensures we'll write proxy correctly in the event of a
# stop/start, and is informative otherwise.
if [[ -n "$http_proxy" ]]; then
  echo "http_proxy=$http_proxy" >>$ID
  resp=$(rsc --key "$refresh_token" --host "$api_hostname" cm15 multi_add /api/tags/multi_add \
       resource_hrefs[]=$server_href \
       resource_hrefs[]=$instance_href \
       tags[]=rs_agent:http_proxy=$http_proxy)
  status=$?
  if [[ "$status" -ne 0  ]]; then
    echo "ERROR: Failed to tag server with rs_agent:http_proxy=$http_proxy, exit code: $status" >&2
    echo "Response: '$resp'" >&2
    exit 1
  fi
fi
if [[ -n "$no_proxy" ]]; then
  echo "no_proxy=$no_proxy" >>$ID
  resp=$(rsc --key "$refresh_token" --host "$api_hostname" cm15 multi_add /api/tags/multi_add \
       resource_hrefs[]=$server_href \
       resource_hrefs[]=$instance_href \
       tags[]=rs_agent:http_no_proxy=$no_proxy)
  status=$?
  if [[ "$status" -ne 0  ]]; then
    echo "ERROR: Failed to tag server with rs_agent:http_no_proxy=$no_proxy, exit code: $status" >&2
    echo "Response: '$resp'" >&2
    exit 1
  fi
fi

# ===== Install RightLink

chmod +x ./install.sh
bash ./install.sh -s -n

# clean up
rm -fr /tmp/rightlink /tmp/rightlink.tgz

echo "`date` Enablement complete."
