#!/usr/bin/env bash

# Usage: p42 start [<component>]
# Summary: Start application component.
# Help: Start application component.
# You can start component for the entire application
# or just a given set of components. Images for each
# container must already exist.
#
#     p42 start
#     p42 start www redis
#
# You can build images first using the build subcommand.
# The run subcommand will build and start component for you.
#

source ${lib}/context.sh

if [ $# -gt 0 ]; then
  components="$@"
else
  if [ ! -d ./launch ]; then
    echo p42: nothing to start
    exit
  fi
  components=$(ls ./launch)
fi

# TODO: what if the swarm master dies?
eval $(docker-machine env --swarm ${cluster}-00)
eval $(aws ecr get-login --region us-east-1) > /dev/null

dns_a() {


  local name ip comment
  local "${@}"

  echo "Adding DNS A record for '${name}'..."

  cat "${clusters}/${cluster}" |\
    yaml set - machine "${name}" |\
    yaml set - ip ${ip} |\
    yaml set - comment "${comment}" |\
    yaml template - $_P42_ROOT/share/dns/a.yaml |\
    yaml json write - > "${tmpDir}/dns-a-${name}.json"

  aws route53 change-resource-record-sets \
    --hosted-zone-id ${dns} \
    --change-batch file:///${tmpDir}/dns-a-${name}.json \
    > /dev/null

}

# TODO: handle entry with no subdomain
dns_alias() {

  local subdomain domain comment
  local "${@}"
  local id=$(aws route53 list-hosted-zones-by-name \
    --dns-name ${domain} \
    --max-items 1 |\
    json HostedZones[0].Id)

  cat "${clusters}/${cluster}" |\
    yaml set - domain "${subdomain}.${domain}" |\
    yaml set - name "${elb_domain}" |\
    yaml set - zone "${elb_hosted_zone}" |\
    yaml set - comment "${comment}" |\
    yaml template - $_P42_ROOT/share/dns/alias.yaml |\
    yaml json write - > "${tmpDir}/dns-alias-${subdomain}.json"

  aws route53 change-resource-record-sets \
    --hosted-zone-id "${id}" \
    --change-batch file:///${tmpDir}/dns-alias-${subdomain}.json \
    > /dev/null

  # Temporary hack--we assume www is also the apex record,
  # so we add a second alias for apex.
  if [ "${sudomain}" = "www" ]; then
    dns_alias \
      subdomain="" \
      domain="${domain}" \
      comment="${comment}"
  fi
}

dns_srv() {
  local protocol public targets comment
  local "${@}"

  echo -n "Adding DNS SRV '${protocol}' record "
  if [ -n "${public}" ]; then
    echo "  for '${public}'..."
  else
    echo "  for zone apex..."
  fi

  cat "${clusters}/${cluster}" |\
    yaml set - protocol "${protocol}" |\
    yaml set - public "${public}" |\
    yaml set - comment "${comment}" |\
    (cat && echo 'targets: ' && \
      printf -- '-  host: %s\n   port: %s\n' $targets) |\
    yaml template - $_P42_ROOT/share/dns/srv.yaml |\
    yaml json write - > "${tmpDir}/dns-srv-${public}.json"

  aws route53 change-resource-record-sets \
    --hosted-zone-id ${dns} \
    --change-batch file:///${tmpDir}/dns-srv-${public}.json \
    > /dev/null

  # Temporary hack--we assume www is also the apex record,
  # so we add a second SRV for the empty value, ex: _._http.
  if [ "${public}" = "www" ]; then
    dns_srv \
      protocol="${protocol}" \
      public="" \
      private="${private}" \
      port="${port}" \
      comment="${comment}"
  fi

}

elb=$(aws elb describe-load-balancers \
  --load-balancer-name "${cluster}" |\
  json 'LoadBalancerDescriptions[0]')

elb_hosted_zone=$(json CanonicalHostedZoneNameID <<<${elb} )
elb_domain=$(json CanonicalHostedZoneName <<<${elb} )

for component in $components; do
  image=$name-$component
  config="./launch/${component}/config.yaml"
  count=$(yaml get "${config}" count)
  subdomains=$(yaml get "${config}" subdomains)

  # For each swarm node, we need to make sure it has a
  # copy of the image. For a private registry, this
  # doesn't happen automatically, so we have to authenticate
  # to each and pull.

  echo "Pulling image '${image}' onto Swarm nodes..."
  machines=$(docker-machine ls \
    --format '{{ .Name }}' \
    --filter "name=${cluster}-")

  for machine in ${machines}; do
    echo "- ${machine}"
    eval $(docker-machine env ${machine})
    eval $(aws ecr get-login --region us-east-1)
    docker pull ${registry}/${image}:latest
  done

  # restore Swarm environment
  eval $(docker-machine env --swarm ${cluster}-00)

  for (( i = 0 ; i < ${count:=1} ; i++ )); do
    instance="${component}-$(printf '%02d' $i)"
    container="${name}-${instance}"
    echo "Starting '${image}' container '${container}'..."

    # TODO: add error handling for Docker errors?
    # TODO: specifying constraints for Swarm deploy?

    if [ -n "${subdomains}" ]; then
      port_option="-p 80:80"
    else
      port_option="-P"
    fi

    docker run \
      ${port_option} \
      --name ${container} \
      --restart always \
      -e AWS_ACCESS_KEY_ID="$(aws configure get aws_access_key_id)" \
      -e AWS_SECRET_ACCESS_KEY="$(aws configure get aws_secret_access_key)" \
      -e AWS_DEFAULT_REGION="$(aws configure get region)" \
      -d ${registry}/${image}:latest

    info=$(docker inspect ${container} | json '[0]')
    ip=$(json 'Node.IP' <<<$info )
    port=$(json 'NetworkSettings.Ports["80/tcp"][0].HostPort' <<<$info )
    node=$(json 'Node.Name' <<<$info )
    aws_info=$(aws ec2 describe-instances \
      --filters "Name=tag-value,Values=${node}" |\
      json 'Reservations[0].Instances[0]')
    id=$(json InstanceId <<<$aws_info )
    ip=$(json PrivateIpAddress <<<$aws_info)

    dns_a \
      name="${container}" \
      ip="${ip}" \
      comment="Added by p42 for stack '${cluster}'"

    # save the list of containers so we can set up
    # DNS SRV records later
    if [ ! "${port}" = "80" ]; then
      targets="${targets} ${container} ${port}"
    fi

    # Add to ELB, if applicable

    if [ -n "${subdomains}" ]; then

      echo "Adding '${container}' to ELB..."

      aws elb register-instances-with-load-balancer \
        --load-balancer-name ${cluster} \
        --instances ${id} >\
        /dev/null

      for subdomain in ${subdomains}; do
          echo "Adding '${subdomain}' alias for ELB..."
          dns_alias \
            subdomain="${subdomain}" \
            domain="${domain}" \
            comment="Added by p42 for stack '${cluster}'"
      done
    fi
  done

  for protocol in $(yaml get "${config}" discovery); do
    dns_srv \
      protocol="${protocol}" \
      public="${component}" \
      targets="${targets}" \
      comment="Added by p42 for stack '${cluster}'"
  done

done
