#!/bin/bash
# Info: Script to install/create/delete/update Kubernetes from file settings 
# Author: JesusPerezLorenzo  
# Release: 1.0 
# Date: 30-10-2023

USAGE="install-kubernetes.sh full-path-settings-file [ -m controlplane (hostname -cp-) | worker]  [*install | update | makejoin | remove | fullremove]"
[ "$1" == "-h"  ] && echo "$USAGE"  && exit 1

_save_target() {
  [ -z "$TARGET_SAVE_PATH" ]  && return
  local file_path=$1
  mkdir -p "$TARGET_SAVE_PATH"
  if cp "$file_path" "$TARGET_SAVE_PATH" ; then 
    echo "$file_path saved in $TARGET_SAVE_PATH"
  fi
}
# shellcheck disable=SC1090
[[ "$1" == *setting* ]] && [ -r  "$1" ]  && . "$1" && shift 
# shellcheck disable=SC1090
[[ "$1" == env-* ]] && [ -r  "$1" ]  && . "$1" && shift 
[ -r "env-kubernetes" ] && . env-kubernetes

[ -z "$CLUSTER_NAME" ] && echo "No CLUSTER_NAME value " && exit 1
[ -z "$VERSION" ] && echo "No VERSION value " && exit 1

INSTALL_LOG=${INSTALL_LOG:-/tmp/k8s.log}
WORK_PATH=${WORK_PATH:-/tmp}
[ ! -d "$WORK_PATH" ] && sudo mkdir -p "$WORK_PATH"
export LC_CTYPE=C.UTF-8 
export LANG=C.UTF-8
cmd_out=/dev/null

echo "Log  path to $INSTALL_LOG"
[ ! -d "$(dirname "$INSTALL_LOG")" ] && mkdir -p "$(dirname "$INSTALL_LOG")"
echo "Work path to $WORK_PATH"

if [ -z "$K8S_MODE" ] ; then 
  if [[ "$HOSTNAME" == *-cp-* ]] ; then
    K8S_MODE="controlplane"
  else
    K8S_MODE="worker"
  fi 
fi
[ "$1" == "-m"  ] && K8S_MODE=$2 && shift 2
[ -n "$1" ] && CMD_TSK=$1 && shift

_check_resolution() {
  local hostname=""
  hostname=$HOSTNAME
  local clustername=""
  local ip=""
  [ "$K8S_MODE" == "controlplane" ] && clustername="$CLUSTER_NAME"
  #sudo sed -i /^127.0.1.1/d /etc/hosts 2>>$cmd_out
  ip=$(grep "$hostname" /etc/hosts | grep -v "^#" | awk '{print $1}')
  [ -n "$ip" ] && [ "$ip" == "127.0.1.1" ] && sudo sed -i /^"$ip"/d /etc/hosts 2>>$cmd_out
  ip=$(grep "$MAIN_IP" /etc/hosts | grep -v "^#" | awk '{print $1}')
  [ -z "$ip" ] && echo "$MAIN_IP $hostname $clustername" | sudo tee -a /etc/hosts 2>>$cmd_out 
  if [ "$hostname" !=  "$(cat /etc/hostname)" ] ; then
    echo "$hostname" | sudo tee /etc/hostname 2>>$cmd_out 
    sudo hostname "$hostname"
  fi
}
_off_swap() {
  local fs_swap
  local fs_tab
  fs_tab=/etc/fstab
  fs_swap=$(grep -v "^#"  $fs_tab | grep swap)
  if [ -n "$fs_swap" ] ; then 
    sudo sed -i "s;$fs_swap;#$fs_swap;g" $fs_tab
  fi
  sudo swapoff -a
}

_kubernetes_init() {
  [ -z "$VERSION" ] && exit 1
  _check_resolution
  curr_vers=$(kubectl version 2>/dev/null | grep Client | awk '{print $3}' | sed 's/^v//g' 2>/dev/null)
  chmod 1777 /tmp 
  if [ "v$curr_vers" != "$K8S_VERSION" ]; then
    echo "Install packages"
    #if [ "$CMD_TSK" != "update" ] && [ ! -r "/etc/apt/keyrings/kubernetes-apt-keyring.gpg" ]; then
      sudo DEBIAN_FRONTEND=noninteractive apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y apt-transport-https gnupg2 curl
      sudo rm -f /etc/apt/keyrings/kubernetes-apt-keyring.gpg
      curl -fsSL https://pkgs.k8s.io/core:/stable:/v"$MAJOR_VERSION"/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
      echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v$MAJOR_VERSION/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    #fi
    _off_swap
    sudo DEBIAN_FRONTEND=noninteractive apt-get update -q 
    sudo DEBIAN_FRONTEND=noninteractive apt-mark unhold kubelet kubectl kubeadm
    if ! sudo DEBIAN_FRONTEND=noninteractive apt-get install -y kubectl kubelet kubeadm ; then
      echo "error installing kubernetes"
      return 1
    fi
    # Hold your horse !
    sudo DEBIAN_FRONTEND=noninteractive apt-mark hold kubelet kubectl kubeadm
    echo "init done"
  fi
}
_kubernetes_taint() {
  case "$TAINT_NODE" in 
    no_schedule) 
      kubectl taint nodes "$HOSTNAME" node-role.kubernetes.io/master:NoSchedule
      ;;
    schedule)
      kubectl taint nodes "$HOSTNAME" node-role.kubernetes.io/master:NoSchedule
      kubectl taint nodes "$HOSTNAME" node-role.kubernetes.io/master:NoSchedule- 2>>$cmd_out
      ;;
  esac
  return 0
}
_kubernetes_cri() {
  [ ! -d "/etc/${K8S_CRI}" ] && echo "No /etc/${K8S_CRI} path found! " && exit 1
  # if [ -r "cri/$K8S_CRI/install.sh" ] ; then
  #   #PKG_ORG=cri/"$K8S_CRI"
  #   echo "cri $K8S_CRI"
  #   # shellcheck disable=SC1090
  #   . "cri/$K8S_CRI/install.sh" | sudo tee -a "$INSTALL_LOG" >>$cmd_out
  # else
  #   echo "$K8S_CRI not defined" && exit 1
  # fi
  return 0
}
_kubernetes_cni() {
  if [ -r "cni/$K8S_CNI/install.sh" ] ; then
    echo "cni $K8S_CNI"
    # shellcheck disable=SC1090
    . "cni/$K8S_CNI/install.sh" | sudo tee -a "$INSTALL_LOG" 2>>$cmd_out
  else
    echo "mode $K8S_CNI not defined" && exit 1
  fi
}
_kubernetes_addons() {
  local yaml_file
  for item in ${K8S_ADDONS//,/ }  #ls addons 2>/dev/null)
  do 
    if [ -r "addons/$item/install.sh" ] ; then 
      echo "Install addon $item "| sudo tee -a "$INSTALL_LOG"
      # shellcheck disable=SC1090
      . "addons/$item/install.sh" 
      if [ "$item"  == "istio" ] && [ -n "$K8S_EXTERNAL_IPS" ]; then
        yaml_file=/tmp/externalIPs.yaml 
        echo "spec:" > $yaml_file
        echo "   externalIPs: " >> $yaml_file
        for ip in ${K8S_EXTERNAL_IPS//,/ }
        do
          echo "   - $ip" >> "$yaml_file"
        done
        # Patch istio ingressgateway to use ExternalIPs 
        kubectl patch service -n istio-system istio-ingressgateway --type merge --patch-file $yaml_file
      fi
    fi
  done
}
_kubernetes_kube() {
  local user=${1:-root}
  local home_user=${2:-/home/root}
  local uid
  local gid
  local has_aliases
  uid=$(sudo id -u "$user" 2>/dev/null)
  gid=$(sudo id -g "$user" 2>/dev/null)
  if [ -f "/etc/kubernetes/admin.conf" ] ; then 
    sudo mkdir -p /root/.kube
    sudo cp /etc/kubernetes/admin.conf /root/.kube/config
    sudo chown root:root /root/.kube/config
    if [ "$uid" == "0" ] ; then 
      mkdir -p "$home_user"/.kube
      sudo cp /etc/kubernetes/admin.conf "$home_user"/.kube/config
      sudo chown -R "$uid:$gid" "$home_user"/.kube
    fi
    has_aliases=$(grep bash_aliases "$HOME"/.bashrc)
    [ -z "$has_aliases" ] && echo "[ -f ~/.bash_aliases ] && . ~/.bash_aliases" | sudo tee -a "$HOME"/.bashrc
    if [ -r "$USER_HOME" ] && [ -n "$USER" ] ; then
      mkdir -p "$USER_HOME"/.kube
      sudo cp /etc/kubernetes/admin.conf "$USER_HOME"/.kube/config
      sudo chown -R "$USER" "$USER_HOME"/.kube
      if [ -r "$USER_HOME/.bash_aliases" ] && [ ! -r "$HOME/.bash_aliases" ] ; then 
        has_aliases=$(grep bash_aliases "$USER_HOME"/.bashrc)
        [ -z "$has_aliases" ] && echo "[ -f ~/.bash_aliases ] && . ~/.bash_aliases" | sudo tee -a "$USER_HOME"/.bashrc
        sudo cp "$USER_HOME"/.bash_aliases "$HOME"
        sudo chown -R "$uid:$gid" "$HOME"/.bash_aliases 
      fi
    fi
  fi
}
_kubectl_appy() {
  export KUBECONFIG=/etc/kubernetes/admin.conf
  [ ! -r "$KUBECONFIG" ] && echo "$KUBECONFIG not found " && return 1
  [ ! -r "$1" ] && echo "File $1 not found" && return 1
  if ! kubectl apply -f "$1" ; then
    echo "Error kubectl apply $1 "
  fi
}
_kubernetes_install_master_0() {
  _check_resolution
  local has_apiserver=""
  has_apiserver=$(sudo ps -aux | awk '{print $11}'| grep "kube-apiserver")
  if [ ! -r "resources/$K8S_CONFIG" ] ; then 
    echo "resources/$K8S_CONFIG not found"
    exit 1
  fi
  if [ "$ETCD_MODE" == "external" ] && [ -d "etcd_certs" ] ; then 
    [ ! -d "/etc/kubernetes/pki/etcd" ] && sudo mkdir -p /etc/kubernetes/pki/etcd
    sudo cp -pr etcd_certs/* /etc/kubernetes/pki/etcd
    if [ -n "$HOSTNAME" ] && [ "$HOSTNAME" != "$INSTALL_MASTER" ] && [ -d "pki" ] ; then
      sudo cp -pr pki/* /etc/kubernetes/pki
    fi
  fi
  echo "Install kubernetes master"
  [ ! -r "resources/$K8S_CONFIG" ] && echo "Error resources/$K8S_CONFIG not found !" && exit 1
  [ "resources/$K8S_CONFIG" != "$WORK_PATH/kubeadm-config.yaml" ] && cp "resources/$K8S_CONFIG" "$WORK_PATH"/kubeadm-config.yaml
  if [ -z "$has_apiserver" ] ; then 
    sudo systemctl start kubelet 2>>$cmd_out
    echo "You can follow kubeadm installation by using in another terminal:  tail -f $INSTALL_LOG"
    sudo kubeadm init --config "$WORK_PATH"/kubeadm-config.yaml --ignore-preflight-errors=all | sudo tee "$INSTALL_LOG"
    _save_target "$WORK_PATH"/kubeadm-config.yaml
  fi
  local has_success=""
  has_success=$(sudo grep "initialized successfully" "$INSTALL_LOG")
  if [ -n "$has_success" ]; then
    echo "$has_success"
    _save_target "$INSTALL_LOG"
    sudo grep -A1 "^kubeadm join" "$INSTALL_LOG" | sudo tee "$WORK_PATH"/k8s_join.sh
    sudo chmod +x "$WORK_PATH/k8s_join.sh"
    [ "$WORK_PATH" != "/tmp" ] && cp "$WORK_PATH/k8s_join.sh" /tmp
    _kubernetes_kube "$(whoami)"   
    _kubernetes_cni
    _kubernetes_addons
    sudo mv "$INSTALL_LOG" "$WORK_PATH"
    [ -r "runtimes.yaml" ] && _kubectl_appy runtimes.yaml
  fi
}
_make_join_kubernetes() {
  if ! kubeadm token create --print-join-command > "$WORK_PATH"/k8s_join.sh ; then 
      echo "Error to get token for join node "
      exit 1
  fi
}
_join_kubernetes() {
  local join_path
  [ -r "k8s_join.sh" ] && join_path="k8s_join.sh" 
  [ -r "/tmp/k8s_join.sh" ] && join_path="/tmp/k8s_join.sh" 
  if [ -r "$join_path" ] ; then
    local cmd_join 
    if [ "$1" == "controlplane" ] ; then 
      cmd_join=$(sed 's/join /join --control-plane /g' < $join_path)
    else
      cmd_join=$(cat $join_path | sed 's/\\//g')
    fi
    [ -z "$cmd_join" ] && echo "Error cmd_join content" && exit 1 
    # shellcheck disable=SC2086
    if ! sudo $cmd_join --ignore-preflight-errors=all | sudo tee "$INSTALL_LOG" >"$cmd_out"; then 
      echo "Error $HOSTNAME join command -> $cmd_join "
      exit 1
    fi
  else
    echo "No k8s_join.sh found"
    return 0
  fi
  return 0
}
_install_kubernetes_controlplane() {
  if [ "$ETCD_MODE" == "external" ] && [ -d "etcd_certs" ] ; then 
    [ ! -d "/etc/kubernetes/pki/etcd" ] && sudo mkdir -p /etc/kubernetes/pki/etcd
    sudo cp -pr etcd_certs/* /etc/kubernetes/pki/etcd
    if [ -n "$HOSTNAME" ] && [ "$HOSTNAME" != "$INSTALL_MASTER" ] && [ -d "pki" ] ; then
      sudo cp -pr pki/* /etc/kubernetes/pki
    fi
  fi
  if ! _join_kubernetes controlplane ; then
    exit 2
  else
    _kubernetes_kube "$USER" "$USER_HOME"  
    _kubernetes_cni
    _kubernetes_addons
  fi
  return 0
}
_install_kubernetes_worker() {
  if ! _join_kubernetes worker ; then
    exit 2
  fi
  return 0
}
_install_kubernetes() { 
  [ ! -d "/etc/${K8S_CRI}" ] && echo "No /etc/${K8S_CRI} path found! " && exit 1
  sudo systemctl start "${K8S_CRI}"
  _check_resolution
  if [ -f "/etc/kubernetes/admin.conf" ] ; then 
    local server=""
    local has_apiserver=""
    has_apiserver=$(sudo ps -aux | awk '{print $11}'| grep "kube-apiserver")
    server=$(sudo grep "server: " /etc/kubernetes/admin.conf | awk '{print $2}')
    echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes already installed in $HOSTNAME with server: $server ($has_apiserver)" | sudo tee -a "$INSTALL_LOG"
    if [ "$CMD_TSK" == "reinstall" ] ; then 
      echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes RESET installation in $HOSTNAME with server: $server ($has_apiserver) ..." | sudo tee -a "$INSTALL_LOG"
      if sudo kubeadm reset -f ; then
        echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes ready to be re-installed in $HOSTNAME " | sudo tee -a "$INSTALL_LOG"
      fi
    else
      _kubernetes_kube "$USER" "$USER_HOME"  
      return
    fi
  elif [ -f "/etc/kubernetes/kubelet.conf" ] ; then 
    echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes kubelet already running in $HOSTNAME"
    if [ "$CMD_TSK" == "reinstall" ] ; then 
      echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes kubelet RESET in $HOSTNAME ..."
      if sudo kubeadm reset -f ; then
        echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes ready to be re-installed in $HOSTNAME " | sudo tee -a "$INSTALL_LOG"
      fi
    else
      return
    fi
  fi
  has_kubelet=$(sudo ps -aux | awk '{print $11}'| grep "kubelet")
  if [ -n "$has_kubelet" ] ; then 
    if [ "$CMD_TSK" == "reinstall" ] ; then 
      if sudo kubeadm reset -f ; then
        echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes ready to be re-installed in $HOSTNAME " | sudo tee -a "$INSTALL_LOG"
      fi
    else
      echo "$(date +%Y_%m_%d_%H%M%S) | Kubernetes kubelet already runnint in $HOSTNAME"
      return
    fi
  fi
  if [ -n "$HOSTNAME" ] && [ "$HOSTNAME" == "$K8S_MASTER" ] ; then 
    #IS_MASTER_0="yes"
    _kubernetes_install_master_0
    _kubernetes_taint 
  else 
    case "$K8S_MODE" in
        controlplane) 
          _install_kubernetes_controlplane 
          _kubernetes_taint
          ;;
        worker) 
          _install_kubernetes_worker 
          ;;
        *) echo "mode $K8S_MODE not defined" && exit 1
      esac
  fi
}
_config_kubernetes() {
  [ ! -d "/etc/${K8S_CRI}" ] && echo "No /etc/${K8S_CRI} path found! " && exit 1
  sudo systemctl start "${K8S_CRI}"
  sudo sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
  has_nolocal_bind=$(sudo grep "net.ipv4.ip_nonlocal_bind = 1" /etc/sysctl.conf)
  if [ -z "$has_nolocal_bind" ] ; then
    echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee -a /etc/sysctl.conf >>$cmd_out
    #echo "net.bridge.bridge-nf-call-iptables=1" | sudo tee -a /etc/sysctl.conf
    sudo modprobe br_netfilter
    echo 1 | sudo tee -a /proc/sys/net/bridge/bridge-nf-call-iptables >>$cmd_out
  fi
  sudo sysctl -p >>$cmd_out
  return 0
}
_remove_kubernetes() {
  sudo systemctl stop kubelet 
  sudo systemctl disable kubelet 
}
_full_remove_kubernetes() {
  _remove_kubernetes
  sudo kubeadm reset -y 
  sudo rm -r /etc/kubernetes /etc/cni
}
_start_kubernetes() {
  if [ "$SYSTEMCTL_MODE" == "enabled" ] ; then 
    sudo systemctl enable kubelet 
  else 
    sudo systemctl disable kubelet 
  fi
  sudo systemctl start kubelet
}
_restart_kubernetes() {
  sudo systemctl restart kubelet
}

case "$CMD_TSK" in 
  remove)
    _remove_kubernetes
    exit 0
    ;;
  fullremove|full-remove)
    _full_remove_kubernetes
    exit 0
    ;;
  update) 
    _restart_kubernetes
    ;;
  makejoin) 
    _make_join_kubernetes
    exit 0
    ;;
  reinstall) ;;
esac
if ! _kubernetes_cri ; then
  echo "error CRI install"
  exit 1
fi
if ! _kubernetes_init ; then
  echo "error kubernetes install"
  exit 1
fi
if ! _config_kubernetes ; then 
  echo "error kubernetes config"
  exit 1
fi
if ! _install_kubernetes ; then 
  echo "error kubernetes install"
  exit 1
fi
if ! _start_kubernetes ; then 
  echo "error kubernetes start"
  exit 1
fi
echo "Work path: $WORK_PATH"
echo "Log info: $INSTALL_LOG"
