#!/usr/bin/env bash

# Fail on error
set -eo pipefail

# Script location and name
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
SCRIPT_NAME=`basename "$0"`

green() {
  echo -e "\033[0;32m$@\033[0m"
}

red() {
  echo -e "\033[0;31m$@\033[0m"
}

yellow() {
  echo -e "\033[0;33m$@\033[0m"
}

cyan() {
  echo -e "\033[0;36m$@\033[0m"
}

# Display help message
help() {
  echo "Setup your cluster for KuboVisor and generate a kubeconfig file."
  echo
  echo "Usage: $SCRIPT_NAME PERMISSION_LEVEL"
  echo "Parameters:"
  echo "PERMISSION_LEVEL     Level of permission to use. Can be \`limited\` or \`read-only\`."
  echo
}

# Check kubectl is available on system
check_kubectl() {
  if test ! command -v kubectl &>/dev/null; then
    red "kubectl was not found on your system!"
    exit 2
  fi
}

# Check authenticated user has required permissions to execute the script without error
check_permissions() {
  cyan "Checking permissions..."

  # Check permission to create namespaces
  if ! kubectl auth can-i create ns &>/dev/null; then
    red "You do not have the permission to create namespaces."
    exit 3
  # Check permission to create service accounts
  elif ! kubectl -n kubovisor auth can-i create sa &>/dev/null; then
    red "You do not have the permission to create service accounts in kubovisor namespace."
    exit 4
  # Check permission to create cluster roles
  elif ! kubectl auth can-i create clusterrole &>/dev/null; then
    red "You do not have the permission to create cluster roles."
    exit 5
  # Check permission to create cluster role bindings
  elif ! kubectl auth can-i create clusterrolebinding &>/dev/null; then
    red "You do not have the permission to create cluster role bindings."
    exit 6
  # Check permission to get namespaces
  elif ! kubectl auth can-i get ns &>/dev/null; then
    red "You do not have the permission to get namespaces."
    exit 7
  # Check permission to get service accounts
  elif ! kubectl -n kubovisor auth can-i get sa &>/dev/null; then
    red "You do not have the permission to get service accounts in kubovisor namespace."
    exit 8
  # Check permission to get cluster roles
  elif ! kubectl auth can-i get clusterrole &>/dev/null; then
    red "You do not have the permission to get cluster roles."
    exit 9
  # Check permission to get cluster role bindings
  elif ! kubectl auth can-i get clusterrolebinding &>/dev/null; then
    red "You do not have the permission to get cluster role bindings."
    exit 10
  # Check permission to get secrets
  elif ! kubectl -n kubovisor auth can-i get secret &>/dev/null; then
    red "You do not have the permission to get secrets in kubovisor namespace."
    exit 11
  fi

  if test "$PERMISSION_LEVEL" = "limited"; then
    # Check permission to create roles
    if ! kubectl -n kubovisor auth can-i create role &>/dev/null; then
      red "You do not have the permission to create roles in kubovisor namespace."
      exit 12
    # Check permission to create role bindings
    elif ! kubectl -n kubovisor auth can-i create rolebinding &>/dev/null; then
      red "You do not have the permission to create role bindings in kubovisor namespace."
      exit 13
    # Check permission to get roles
    elif ! kubectl -n kubovisor auth can-i get role &>/dev/null; then
      red "You do not have the permission to get roles in kubovisor namespace."
      exit 14
    # Check permission to get role bindings
    elif ! kubectl -n kubovisor auth can-i get rolebinding &>/dev/null; then
      red "You do not have the permission to get role bindings in kubovisor namespace."
      exit 15
    fi
  fi
}

# Fail if no parameters given to script
if test $# -ne 1; then
  help
  exit 1
fi

# Get user-provided parameters
PERMISSION_LEVEL=$1

if test "$PERMISSION_LEVEL" != "limited" && test "$PERMISSION_LEVEL" != "read-only"; then
  red "Invalid permission level provided: $PERMISSION_LEVEL."
  help
  exit 1
fi

# Execute checks before executing script
check_kubectl
check_permissions

# Create namespace and service account
if ! kubectl get ns kubovisor &>/dev/null; then
  kubectl create ns kubovisor
else
  echo "Namespace already exists."
fi

if ! kubectl -n kubovisor get sa ksa-kubovisor &>/dev/null; then
  kubectl -n kubovisor create sa ksa-kubovisor
else
  echo "Service account already exists."
fi

# Create permissions needed for both permission levels
if ! kubectl get clusterrole kubovisor &>/dev/null; then
  kubectl create clusterrole kubovisor --verb=list,get,watch --resource='*.*'
  # We need to patch whole rules because cluster role does not support strategic merge patches
  kubectl patch clusterrole kubovisor --patch '{"rules":[{"apiGroups":["*"],"resources":["*"],"verbs":["list","get","watch"]},{"nonResourceURLs":["/metrics"],"verbs":["get"]}]}'
else
  yellow "Cluster role already exists."
fi

if ! kubectl get clusterrolebinding kubovisor &>/dev/null; then
  kubectl create clusterrolebinding kubovisor --clusterrole=kubovisor --serviceaccount=kubovisor:ksa-kubovisor
else
  yellow "Cluster role binding already exists."
fi

# Create permissions needed by limited level
if test "$PERMISSION_LEVEL" = "limited"; then
  if ! kubectl -n kubovisor get role kubovisor-limited &>/dev/null; then
    kubectl -n kubovisor create role kubovisor-limited --verb=create,get,delete --resource=pods,pods/log
  else
    yellow "Role already exists."
  fi

  if ! kubectl -n kubovisor get rolebinding kubovisor-limited &>/dev/null; then
    kubectl -n kubovisor create rolebinding kubovisor-limited --role=kubovisor-limited --serviceaccount=kubovisor:ksa-kubovisor
  else
    yellow "Role binding already exists."
  fi
fi

# Remove permissions not needed by read-only level
if test "$PERMISSION_LEVEL" = "read-only"; then
  if kubectl -n kubovisor get rolebinding kubovisor-limited &>/dev/null; then
    cyan "Role binding kubovisor-limited exists, trying to remove it..."

    if ! kubectl -n kubovisor delete rolebinding kubovisor-limited &>/dev/null; then
      red "Could not remove existing role binding kubovisor-limited. The service account may have more permissions than needed."
    fi
  fi
fi



SERVICE_ACCOUNT=ksa-kubovisor
NAMESPACE=kubovisor

# Create secret for the service account w
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: $SERVICE_ACCOUNT
  namespace: $NAMESPACE
  annotations:
    kubernetes.io/service-account.name: $SERVICE_ACCOUNT
type: kubernetes.io/service-account-token
EOF


### Generate kubeconfig file ###

cyan "Generating kubeconfig..."

# Prepare data needed to generate kubeconfig file
TOKEN=$(kubectl -n $NAMESPACE get secret $SERVICE_ACCOUNT -o jsonpath='{.data.token}' | base64 -d)
CA_CERT=$(kubectl -n $NAMESPACE get secret $SERVICE_ACCOUNT -o jsonpath='{.data.ca\.crt}')
CONTEXT=$(kubectl config current-context)
CLUSTER=$(kubectl config view -o "jsonpath={.contexts[?(@.name==\"$CONTEXT\")].context.cluster}")
SERVER=$(kubectl config view -o "jsonpath={.clusters[?(@.name==\"$CLUSTER\")].cluster.server}")

# Generate kubeconfig from previously gathered data
KUBECONFIG=$(mktemp)
cat <<EOF > $KUBECONFIG
apiVersion: v1
kind: Config
clusters:
- name: $CLUSTER
  cluster:
    certificate-authority-data: $CA_CERT
    server: $SERVER
contexts:
- name: $CONTEXT
  context:
    cluster: $CLUSTER
    namespace: $NAMESPACE
    user: $SERVICE_ACCOUNT
current-context: $CONTEXT
users:
- name: $SERVICE_ACCOUNT
  user:
    token: $TOKEN
EOF

# Move generated kubeconfig to a file in current working directory
mv $KUBECONFIG $(pwd)/kubeconfig-$SERVICE_ACCOUNT.yaml

# Display generated kubeconfig location to user
green "Kubeconfig file successfully generated: $(pwd)/kubeconfig-$SERVICE_ACCOUNT.yaml"
