#!/bin/bash
## START_OF_BASH_PLUS
red="\033[38;5;196m"
neutral='\033[0m'
cyan='\033[00;34m'
white='\033[97m'
green="\033[38;5;46m"
ok=${green}"✔"${neutral}
# fail=${red}"✖"${neutral}

epoch () {
  echo `python -c "import time; print  ('{:0.0f}'.format(time.time()))"`
}

stopwatch () {
  BEGIN=$1
  NOW=`epoch`
  let DIFF=$(( $NOW - $BEGIN ))
  echo $DIFF
}

info () {
  >&2 printf " [ ${cyan}INFO${neutral} ] $1 $2\n"
}

user () {
  # shellcheck disable=SC2059
 >&2 printf "\r  [ \033[0;33m?\033[0m ] $1 "
}

success () {
  >&2 printf "   [ ${green}OK${neutral} ] $1 $2\n"
}

fatal () {
  error $2
  exit 1
}

error(){
 >&2  printf "[ ${red}ERROR${neutral} ] $1 $2\n"
}

# log a message with a seconds since program start prefix
log() {
  if [[ "$START" == "" ]]; then
      START=$(epoch)
  fi
  >&2 printf "[ $(printf %05d $(stopwatch $START)) ] $1\n"
}


die(){
    printf ${red}"$1${neutral} "$2"\n"
    exit 1
}

hr(){
  >&2   echo "================================================================================"
}

hr2(){
  >&2   echo "=================================================="
}

hr3(){
  >&2   echo "========================================"
}

strict_mode() {

  set -o errexit
  set -o nounset
  set -o pipefail
}

strict_mode_off() {
  unset -o errexit
  unset -o nounset
  unset -o pipefail
}

is_linux(){
    if [ "$(uname -s)" = "Linux" ]; then
        return 0
    else
        return 1
    fi
}

is_mac(){
    if [ "$(uname -s)" = "Darwin" ]; then
        return 0
    else
        return 1
    fi
}

is_jenkins(){
    if [ -n "${JENKINS_URL:-}" ]; then
        return 0
    else
        return 1
    fi
}

is_travis(){
    if [ -n "${TRAVIS:-}" ]; then
        return 0
    else
        return 1
    fi
}

is_CI(){
    if [ -n "${CI:-}" -o -n "${CI_NAME:-}" ] || is_jenkins || is_travis; then
        return 0
    else
        return 1
    fi
}
timestamp=$(date +%s)
## END_OF_BASH_PLUS
#!/bin/bash
# ARG_OPTIONAL_SINGLE([cluster-name],[n],[cluster name, must be unique])
# ARG_OPTIONAL_BOOLEAN([bootstrap-master],[],[Whether to create a new primary master],[on])
# ARG_OPTIONAL_SINGLE([workers],[w],[number of worker nodes],[3])
# ARG_OPTIONAL_SINGLE([masters],[m],[number of secondary worker modes],[1])
# ARG_OPTIONAL_SINGLE([secondary],[s],[number of secondary worker modes],[2])
# ARG_OPTIONAL_SINGLE([config],[c],[Config file with options],[])
# ARG_OPTIONAL_SINGLE([host-prefix],[p],[prefix to add to every hostname],[])
# ARG_OPTIONAL_SINGLE([master-label],[],[Label to add to all masters],[m-])
# ARG_OPTIONAL_SINGLE([secondary-label],[],[Label to add to all secondary masters],[s-])
# ARG_OPTIONAL_SINGLE([worker-label],[],[Label to add to all worker nodes],[w-])
# ARG_OPTIONAL_SINGLE([consul-ip],[],[The IP address of consul to use for master discovery, if not specified a new consul server will be created],[])
# ARG_OPTIONAL_REPEATED([extra],[e],[set additional variables as key=value or YAML/JSON, if filename prepend with @],[])
# ARG_OPTIONAL_SINGLE([host-suffix-format],[],[Date format to use as the suffix for each host],[+%m%d%H%M%S])
# ARG_OPTIONAL_SINGLE([provisioner],[],[The provisioner to use when launching instances],[ansible-provision])
# ARG_OPTIONAL_BOOLEAN([debug],[],[boolean optional argument help msg])
# ARG_OPTIONAL_SINGLE([k8s-version],[],[The version of k8s to deploy])
# ARG_HELP([])
#start_region
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.8.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info


die()
{
	local _ret=$2
	test -n "$_ret" || _ret=1
	test "$_PRINT_HELP" = yes && print_help >&2
	echo "$1" >&2
	exit ${_ret}
}


begins_with_short_option()
{
	local first_option all_short_options='nwmscpeh'
	first_option="${1:0:1}"
	test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}

# THE DEFAULTS INITIALIZATION - OPTIONALS
cluster_name=
bootstrap_master="on"
workers="3"
masters="1"
secondary="2"
config=
host_prefix=
master_label="m-"
secondary_label="s-"
worker_label="w-"
consul_ip=
extra=()
host_suffix_format="+%m%d%H%M%S"
provisioner="ansible-provision"
debug="off"
k8s_version=


print_help()
{
	printf 'Usage: %s [-n|--cluster-name <arg>] [--(no-)bootstrap-master] [-w|--workers <arg>] [-m|--masters <arg>] [-s|--secondary <arg>] [-c|--config <arg>] [-p|--host-prefix <arg>] [--master-label <arg>] [--secondary-label <arg>] [--worker-label <arg>] [--consul-ip <arg>] [-e|--extra <arg>] [--host-suffix-format <arg>] [--provisioner <arg>] [--(no-)debug] [--k8s-version <arg>] [-h|--help]\n' "$0"
	printf '\t%s\n' "-n, --cluster-name: cluster name, must be unique (no default)"
	printf '\t%s\n' "--bootstrap-master, --no-bootstrap-master: Whether to create a new primary master (on by default)"
	printf '\t%s\n' "-w, --workers: number of worker nodes (default: '3')"
	printf '\t%s\n' "-m, --masters: number of secondary worker modes (default: '1')"
	printf '\t%s\n' "-s, --secondary: number of secondary worker modes (default: '2')"
	printf '\t%s\n' "-c, --config: Config file with options (no default)"
	printf '\t%s\n' "-p, --host-prefix: prefix to add to every hostname (no default)"
	printf '\t%s\n' "--master-label: Label to add to all masters (default: 'm-')"
	printf '\t%s\n' "--secondary-label: Label to add to all secondary masters (default: 's-')"
	printf '\t%s\n' "--worker-label: Label to add to all worker nodes (default: 'w-')"
	printf '\t%s\n' "--consul-ip: The IP address of consul to use for master discovery, if not specified a new consul server will be created (no default)"
	printf '\t%s\n' "-e, --extra: set additional variables as key=value or YAML/JSON, if filename prepend with @ (empty by default)"
	printf '\t%s\n' "--host-suffix-format: Date format to use as the suffix for each host (default: '+%m%d%H%M%S')"
	printf '\t%s\n' "--provisioner: The provisioner to use when launching instances (default: 'ansible-provision')"
	printf '\t%s\n' "--debug, --no-debug: boolean optional argument help msg (off by default)"
	printf '\t%s\n' "--k8s-version: The version of k8s to deploy (no default)"
	printf '\t%s\n' "-h, --help: Prints help"
}


parse_commandline()
{
	while test $# -gt 0
	do
		_key="$1"
		case "$_key" in
			-n|--cluster-name)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				cluster_name="$2"
				shift
				;;
			--cluster-name=*)
				cluster_name="${_key##--cluster-name=}"
				;;
			-n*)
				cluster_name="${_key##-n}"
				;;
			--no-bootstrap-master|--bootstrap-master)
				bootstrap_master="on"
				test "${1:0:5}" = "--no-" && bootstrap_master="off"
				;;
			-w|--workers)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				workers="$2"
				shift
				;;
			--workers=*)
				workers="${_key##--workers=}"
				;;
			-w*)
				workers="${_key##-w}"
				;;
			-m|--masters)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				masters="$2"
				shift
				;;
			--masters=*)
				masters="${_key##--masters=}"
				;;
			-m*)
				masters="${_key##-m}"
				;;
			-s|--secondary)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				secondary="$2"
				shift
				;;
			--secondary=*)
				secondary="${_key##--secondary=}"
				;;
			-s*)
				secondary="${_key##-s}"
				;;
			-c|--config)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				config="$2"
				shift
				;;
			--config=*)
				config="${_key##--config=}"
				;;
			-c*)
				config="${_key##-c}"
				;;
			-p|--host-prefix)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				host_prefix="$2"
				shift
				;;
			--host-prefix=*)
				host_prefix="${_key##--host-prefix=}"
				;;
			-p*)
				host_prefix="${_key##-p}"
				;;
			--master-label)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				master_label="$2"
				shift
				;;
			--master-label=*)
				master_label="${_key##--master-label=}"
				;;
			--secondary-label)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				secondary_label="$2"
				shift
				;;
			--secondary-label=*)
				secondary_label="${_key##--secondary-label=}"
				;;
			--worker-label)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				worker_label="$2"
				shift
				;;
			--worker-label=*)
				worker_label="${_key##--worker-label=}"
				;;
			--consul-ip)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				consul_ip="$2"
				shift
				;;
			--consul-ip=*)
				consul_ip="${_key##--consul-ip=}"
				;;
			-e|--extra)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				extra+=("$2")
				shift
				;;
			--extra=*)
				extra+=("${_key##--extra=}")
				;;
			-e*)
				extra+=("${_key##-e}")
				;;
			--host-suffix-format)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				host_suffix_format="$2"
				shift
				;;
			--host-suffix-format=*)
				host_suffix_format="${_key##--host-suffix-format=}"
				;;
			--provisioner)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				provisioner="$2"
				shift
				;;
			--provisioner=*)
				provisioner="${_key##--provisioner=}"
				;;
			--no-debug|--debug)
				debug="on"
				test "${1:0:5}" = "--no-" && debug="off"
				;;
			--k8s-version)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				k8s_version="$2"
				shift
				;;
			--k8s-version=*)
				k8s_version="${_key##--k8s-version=}"
				;;
			-h|--help)
				print_help
				exit 0
				;;
			-h*)
				print_help
				exit 0
				;;
			*)
				_PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
				;;
		esac
		shift
	done
}

parse_commandline "$@"

# OTHER STUFF GENERATED BY Argbash

### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash
#end_region


ARGS=

for e in "${extra[@]}"; do
  ARGS+=" -e $e"
done

logv "Verbose log"

k8s_version=${k8s_version:-v1.13.6}

LOCAL_IP=$(ifconfig | sed -n 's/.*inet addr:\([0-9.]\+\)\s.*/\1/p' |grep -v 127.0.0.1)

[[ "$cluster_name" == "" ]] && die "Must specify --cluster-name"
[[ "$config" != "" ]] && ARGS+=" --config $config"
[[ "$debug" != "" ]] && ARGS+=" --debug"

[[ "$consul_ip" == "" ]] && consul_ip=$($provisioner --hostname ${host_prefix}consul-server --group consul --print-ip $ARGS)

[[ "$consul_ip" == "" ]] && die "Failed to provision consul"
[[ -e ~/.kube-provision ]] || git clone https://github.com/moshloop/kube-provision.git $HOME/.kube-provision

ARGS+=" -e consul_ip=$consul_ip -e consul_server_http=$consul_ip:8500"

log "Creating new cluster $cluster_name:$k8s_version with masters:$masters and workers:$workers, consul:$consul_ip"
mkdir -p $PWD/$cluster_name
kubeadm init phase certs all \
   --cert-dir $PWD/$cluster_name/certs \
   --apiserver-cert-extra-sans "localhost" \
   --apiserver-cert-extra-sans "127.0.0.1"

token=$(kubeadm token generate)
if [[ -e $cluster_name/bootstrap_token ]]; then
  token=$(cat $cluster_name/bootstrap_token)
else
  echo $token > $cluster_name/bootstrap_token
fi

ARGS+=" -e token=$token -e cluster_name=$cluster_name -e kubernetes_version=${k8s_version}"

ARGS+=" -i $HOME/.kube-provision/inventory -e hooks=$HOME/.kube-provision"

# ansible doesn't look up templates relative to the playbook, but relative to the calling directory
# so we create a temporary copy
for template in $(ls $HOME/.kube-provision/templates ); do
	src=$HOME/.kube-provision/templates/$template
	target=templates/$template
	ln -s $src $target
	delete_on_exit $target
done

[[ -e inventory ]] && ARGS+=" -i $PWD/inventory"

if [[ "$masters" -gt 0  ]]; then
  master_ip=$($provisioner --host-prefix ${host_prefix}${master_label}${cluster_name} \
             --group primary-master \
             --print-ip $ARGS)
  echo $master_ip > $cluster_name/master_ip
  log "Provisioned master node: $master_ip"
fi
master_ip=$(cat $cluster_name/master_ip)

[[ "$master_ip" == "" ]] && die "Failed to provision master or retrieve ip"
export KUBECONFIG=$PWD/$cluster_name/admin.conf

if [[ ! -e $KUBECONFIG ]]; then
  kubeadm init phase kubeconfig admin --cert-dir $PWD/$cluster_name/certs --apiserver-advertise-address $master_ip  --kubeconfig-dir $PWD/$cluster_name
	sed "s|kubernetes|$name|" -i $KUBECONFIG
	sed "s|@kubernetes||" -i $KUBECONFIG
fi

# [[ -e deploy.sh ]] && ./deploy.sh
secondary_masters=$secondary

if [[  "$secondary_masters" -gt 0 ]]; then
	log "Provisioning $secondary_masters secondary masters "
  for i in $(seq 1 $secondary_masters); do
    $provisioner --group secondary-master \
								 -e wait_for_ip=false \
                 --host-prefix ${host_prefix}${secondary_label}${cluster_name} \
                $ARGS
  done
fi
log "Provisioning $workers workers"
if [[ "$workers" -gt 0 ]]; then
  for i in $(seq 1 $workers); do
    $provisioner --group worker \
                 --host-prefix ${host_prefix}${worker_label}${cluster_name} \
							 	 -e wait_for_ip=false \
                 $ARGS
  done
fi
# ] <-- needed because of Argbash
