#!/bin/bash
# update node's config files after migrate master or etcd cluster

# update 2017-05-18: support for updating only the config files of kubelet and kube-proxy, or modifying only the config file of flannel
# create 2017-03-30

function mylogger()
{
  logger -t "update-node-configs.sh [$$]" "$*" >/dev/null 2>&1 || :
  echo -e "$*"
}

function add_config_files() {
    CONFIG_FILES[${#CONFIG_FILES[@]}]="$1"
}

function add_rollback_action() {
    ROLLBACK_ACTION[${#ROLLBACK_ACTION[@]}]="$1"
}

function rollback() {
    mylogger "\033[33mStart rollback:\033[0m"
    declare -i i=$STEP-1
    declare -i size=0
    last_run=()
    while [ $i -ge 0 ];do
        if [ "${ROLLBACK_ACTION[$i]:0:1}" = "@" ];then
            last_run[$size]="${ROLLBACK_ACTION[$i]:1}"
            let size++
        else
            eval "${ROLLBACK_ACTION[$i]}"
            _r=$?
            if [ "$_r" = "0" ];then
                mylogger "\033[32msuccess: ${ROLLBACK_ACTION[$i]}\033[0m" 
            else
                mylogger "\033[31mfailed: ${ROLLBACK_ACTION[$i]}\033[0m" 
            fi
        fi
        let i--
    done
    for ((i=0; i<${#last_run[@]}; i++)); do
        eval "${last_run[i]}"
        _r=$?
        if [ "$_r" = "0" ];then
            mylogger "\033[32msuccess: ${last_run[i]}\033[0m" 
        else
            mylogger "\033[31mfailed: ${last_run[i]}\033[0m" 
        fi
    done
    mylogger "\033[33mRollback finished\033[0m"
    exit 1
}

function pre_check() {
    mylogger "\033[36mStep $STEP: check config files\033[0m"
    flag=0
    for f in "${CONFIG_FILES[@]}";do
        if [ ! -f "$f" ]; then
            mylogger "config file doesn't exist: $f"
            flag=1
        else
            mylogger "\033[32m$f exists.\033[0m"
        fi
    done
    if [[ $flag -eq 1 ]];then
        exit 1
    fi
    let STEP++
}

function update_kube_config() {
    # update /etc/sysconfig/kubelet
    mylogger "\033[36mStep $STEP: update config: /etc/sysconfig/kubelet\033[0m"
    /bin/cp /etc/sysconfig/kubelet /tmp/kubelet.bak
    /bin/sed -i "s#^API_SERVERS=.*#API_SERVERS='--api-servers=$NEW_MASTER'#" /etc/sysconfig/kubelet || rollback
    let STEP++
    # update /etc/sysconfig/kube-proxy
    mylogger "\033[36mStep $STEP: update config: /etc/sysconfig/kube-proxy\033[0m"
    /bin/cp /etc/sysconfig/kube-proxy /tmp/kube-proxy.bak
    /bin/sed -i "s#^KUBE_MASTER=.*#KUBE_MASTER='--master=$NEW_MASTER'#" /etc/sysconfig/kube-proxy || rollback
    let STEP++
    # restart kube services
    systemctl daemon-reload
    mylogger "\033[36mStep $STEP: restart service: kubelet\033[0m"
    systemctl restart kubelet
    sleep 5
    systemctl status -l kubelet || rollback
    let STEP++
    mylogger "\033[36mStep $STEP: restart service: kube-proxy\033[0m"
    systemctl restart kube-proxy
    sleep 5
    systemctl status -l kube-proxy || rollback
    let STEP++
}

function update_flannel_config() {
    # update /etc/sysconfig/flanneld
    mylogger "\033[36mStep $STEP: update config: /etc/sysconfig/flanneld\033[0m"
    /bin/cp /etc/sysconfig/flanneld /tmp/flanneld.bak
    /bin/sed -i "s#^FLANNEL_ETCD=.*#FLANNEL_ETCD=\"$NEW_ETCD\"#" /etc/sysconfig/flanneld || rollback
    let STEP++
    # restart flanneld service
    mylogger "\033[36mStep $STEP: restart service: flanneld\033[0m"
    systemctl daemon-reload
    systemctl disable flanneld
    systemctl restart flanneld
    systemctl enable flanneld
    sleep 5
    systemctl status flanneld || rollback
    let STEP++
}

function show_help ()
{
    echo "
Usage:
    update-node-configs.sh [options]

Available Commands:
    help        show the help information about update-node-configs.sh.sh

Options:
    --master    The new master address ('http://ip:port' or 'https://ip:port').
    --etcd      The new etcd cluster (http://ip:port,http://ip:port).

Usage Example:
    bash update-node-configs.sh --master=http://192.168.56.101:8080 --etcd=http://192.168.56.101:4012,http://192.168.56.102:4012
"
}

function main() {
    mylogger "\033[33mbegin\033[0m"
    mylogger "\033[33mbash $0 $*\033[0m"
    # 'STEP' controll the rollback action
    STEP=0
    NEW_MASTER=
    NEW_ETCD=
    CONFIG_FILES=()
    # '@' holds the command should be execute at the end of the rollback process
    ROLLBACK_ACTION=()
    while (($#>0)); do
        case "$1" in
        --master=*) NEW_MASTER=${1#--master=} ;;
        --etcd=*) NEW_ETCD=${1#--etcd=} ;;
        --help*) show_help; exit 0 ;;
        -h) show_help; exit 0 ;;
        esac
        shift
    done
    if [ -n "$NEW_MASTER" ]; then
        add_config_files '/etc/sysconfig/kubelet'
        add_config_files '/etc/sysconfig/kube-proxy'
        add_rollback_action '/bin/cp /tmp/kubelet.bak /etc/sysconfig/kubelet'
        add_rollback_action '/bin/cp /tmp/kube-proxy.bak /etc/sysconfig/kube-proxy'
        add_rollback_action '@systemctl restart kubelet; sleep 5; systemctl status -l kubelet'
        add_rollback_action '@systemctl restart kube-proxy; sleep 5; systemctl status -l kube-proxy'
    fi
    if [ -n "$NEW_ETCD" ]; then
        add_config_files '/etc/sysconfig/flanneld'
        add_rollback_action '/bin/cp /tmp/flanneld.bak /etc/sysconfig/flanneld'
        add_rollback_action '@systemctl daemon-reload; systemctl disable flanneld; systemctl restart flanneld; systemctl enable flanneld; sleep 5; systemctl status -l flanneld'
    fi
    pre_check
    [ -n "$NEW_MASTER" ] && update_kube_config
    [ -n "$NEW_ETCD" ] && update_flannel_config
    mylogger "\033[33mupdate success\033[0m"
    mylogger "\033[33mend\033[0m"
}

main "$@"
