#!/bin/bash

if [ -n “${CHEF_ENVIRONMENTS}” ]; then

environments=( ${CHEF_ENVIRONMENTS} )

elif [ -f “${PWD%/}/.travis-envs” ]; then

environments=( $(<"${PWD%/}/.travis-envs") )

fi

decrypt() {

if [ -n "${SECRET}" ]; then
  openssl aes-256-cbc -pass 'env:SECRET' -in "${1}" -out "${2}" -d -a
  return $?
else
  return 1
fi

}

load_ssh_key() {

decrypt "${PWD%/}/.travis-key" ~/.ssh/id_rsa_travis || return $?
chmod 0600 ~/.ssh/id_rsa_travis || return $?
ssh-add -D || return $?
ssh-add ~/.ssh/id_rsa_travis || return $?
return 0

}

update_nodes() {

knife exec -E "def get_value(node, attribute);
  attribute.split('.').inject(node) { |hash, key| hash[key] };
end;
def update_node(node, attribute, value);
  begin;
    puts \"Updating #{node.name}[#{attribute}] to #{value}\";
    attribute.split('.').inject(node.normal) { |hash, key|
      hash[key].is_a?(String) ? hash[key] = value : hash[key];
    };
    saved_node = node.save;
    loaded_node = Chef::Node.load(node.name);
    unless get_value(saved_node, attribute) == value
      puts \"Warning: saved node value does not match #{value}\";
      return false;
    end;
    unless get_value(loaded_node, attribute) == value
      puts \"Warning: loaded node value does not match #{value}\";
      return false;
    end;
    return true;
  rescue;
  end;
  return false;
end;
nodes.find('${1}') { |node|
  succeeded = false
  (1..5).each { |try|
    puts \"Attempt #{try} for #{node.name}:\";
    succeeded = update_node(node, '${2}', '${3}');
    break if succeeded;
  };
  if succeeded;
    puts \"Updated #{node.name}\";
  else;
    fail(StandardError, \"Failed to update #{node.name}\");
  end;
}"
return $?

}

setup_chef() {

if [ -n "${CHEF_ENVIRONMENTS}" ]; then
  setup_chef_from_env
else
  setup_chef_from_files
fi

}

setup_chef_from_files() {

# Load ssh key for knife
load_ssh_key || exit $?

if [ -n "${CHEF_VALIDATOR}" ]; then
  CHEF_VALIDATOR="${PWD%/}/.travis-${CHEF_VALIDATOR}"
else
  CHEF_VALIDATOR="$(echo ${PWD%/}/.travis-*-validator.pem)"
fi

# Determine Chef organization name from validator pem
CHEF_ORG="${CHEF_VALIDATOR##*/.travis-}"
CHEF_ORG="${CHEF_ORG%% }"
CHEF_ORG="${CHEF_ORG%%-validator*}"

# Set CHEF_SERVER from CHEF_ORG unless provided from environment
CHEF_SERVER="${CHEF_SERVER:-https://api.opscode.com/organizations/${CHEF_ORG}}"

if [ -n "${CHEF_CLIENT}" ]; then
  CHEF_CLIENT="${PWD%/}/.travis-${CHEF_CLIENT}"
else
  # Determine Chef node name from client pem
  CHEF_CLIENT="$(echo ${PWD%/}/.travis-*.pem)"
  CHEF_CLIENT="${CHEF_CLIENT/${CHEF_VALIDATOR}/}"
fi

# Determine Chef user name from client pem
CHEF_USER="${CHEF_CLIENT##*/.travis-}"
CHEF_USER="${CHEF_USER%% }"
CHEF_USER="${CHEF_USER%%.*}"

# Configure knife and decrypt keys
[ -d "${PWD%/}/.chef" ] && rm -rf "${PWD%/}/.chef"
mkdir "${PWD%/}/.chef" || exit $?
cat >"${PWD%/}/.chef/knife.rb" <<EOF

current_dir = File.dirname(__FILE__)

log_level :info log_location STDOUT

node_name '${CHEF_USER}' client_key “#{current_dir}/${CHEF_USER}.pem” validation_client_name '${CHEF_ORG}-validator' validation_key “#{current_dir}/${CHEF_ORG}-validator.pem” chef_server_url '${CHEF_SERVER}'

knife = '${CHEF_USER}' knife = '~/.ssh/id_rsa_travis' EOF

decrypt ${CHEF_CLIENT} "${PWD%/}/.chef/${CHEF_USER}.pem" || exit $?
decrypt ${CHEF_VALIDATOR} "${PWD%/}/.chef/${CHEF_ORG}-validator.pem" || exit $?

}

setup_chef_from_env() {

# Install chef gem to get knife
gem install chef -v '~> 11.0' --no-rdoc --no-ri || exit $?

# Redefine CHEF_SERVER if possible using environment
if [ -n "${environment}" ]; then
  chef_server_variable_name="CHEF_SERVER_${environment^^}"
  if [ -n "${!chef_server_variable_name}" ]; then
    CHEF_SERVER="${!chef_server_variable_name}"
  fi
fi

# Determine Chef organization name from CHEF_SERVER
CHEF_ORG="${CHEF_SERVER##*/}"

# Redefine CHEF_USER if possible using environment
if [ -n "${environment}" ]; then
  chef_user_variable_name="CHEF_USER_${environment^^}"
  if [ -n "${!chef_user_variable_name}" ]; then
    CHEF_USER="${!chef_user_variable_name}"
  fi
fi

# Redefine CHEF_CLIENT if possible using environment
if [ -n "${environment}" ]; then
  chef_client_variable_name="CHEF_CLIENT_${environment^^}"
  if [ -n "${!chef_client_variable_name}" ]; then
    CHEF_CLIENT="${!chef_client_variable_name}"
  fi
fi

# Configure knife and write client key
[ -d "${PWD%/}/.chef" ] && rm -rf "${PWD%/}/.chef"
mkdir "${PWD%/}/.chef" || exit $?
if [ -n "${CHEF_CLIENT}" ]; then
  echo -e "${CHEF_CLIENT}" >"${PWD%/}/.chef/${CHEF_USER}.pem"
fi
cat >"${PWD%/}/.chef/knife.rb" <<EOF

current_dir = File.dirname(__FILE__)

log_level :info log_location STDOUT

node_name '${CHEF_USER}' client_key “#{current_dir}/${CHEF_USER}.pem” chef_server_url '${CHEF_SERVER}'

knife = '${CHEF_USER}' EOF }

if [ -n “${1}” -a “${1}” = “prepare” ]; then

load_ssh_key
exit $?

fi

if [ -n “${1}” -a “${1}” = “setup” ]; then

setup_chef
exit $?

fi

if [ -n “${TRAVIS_BRANCH}” ]; then

if [ "${TRAVIS_BRANCH%%/*}" = "environment" ]; then
  # Only run once on matrix builds
  if [ -n "${TEST_GROUP}" -a "${TEST_GROUP}" != "1" ]; then
    exit 0
  fi

  # Deploy if we support the environment
  environment="${TRAVIS_BRANCH##environment/}"
  if [[ "${environments[@]}" != "${environments[@]//${environment}/}" ]]; then
    setup_chef

    # Run the app-specific deploy code
    source "${PWD%/}/.travis-deploy"
  fi
else
  # Not a deploy so just run the tests
  source "${PWD%/}/.travis-test"
fi

else

environment="${1}"
if [[ "${environments[@]}" == "${environments[@]//${environment}/}" ]]; then
  set +x
  echo "Usage: $(basename "${0}") ENVIRONMENT"
  echo
  echo "  Valid environments:"
  for e in ${environments[@]}; do
    echo "    ${e}"
  done
  echo
  echo "Report bugs to <mattk@granicus.com>."
  exit 0
fi

fi

exit 0