#!/bin/bash

# Copyright 2021 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Gather data comparing the overhead of multiple local Kubernetes (macOS and linux)
readonly TESTS=$1

# How many iterations to cycle through
readonly TEST_ITERATIONS=10

# How long to poll CPU usage for (each point is an average over this period)
readonly POLL_DURATION=5s

# How long to measure background usage for. 5 minutes too short, 10 minutes too long
readonly TOTAL_DURATION=5m

# How all tests will be identified
readonly SESSION_ID="$(date +%Y%m%d-%H%M%S)-$$"

# OS Type
readonly OS=$(uname)

measure() {
  local name=$1
  local iteration=$2
  local totalduration=$3
  local filename="benchmark-results/${SESSION_ID}/cstat.${name}.$$-${iteration}"

  echo ""
  echo "  >> Current top processes by CPU:"
  if [[ "${OS}" == "Darwin" ]]; then
    top -n 3 -l 2 -s 2 -o cpu  | tail -n4 | awk '{ print $1 " " $2 " " $3 " " $4 }'
  elif [[ "${OS}" == "Linux" ]]; then
    top -b -n 3 -o %CPU | head -n 9
  fi

  if [[ "${iteration}" == 0 ]]; then
    echo "NOTE: dry-run iteration: will not record measurements"
    cstat --poll "${POLL_DURATION}" --for "${POLL_DURATION}" --busy
    return
  fi

  echo ""
  echo "  >> Measuring ${name} and saving to out/${filename} ..."
  if [[ "${totalduration}" != "" ]]; then
    cstat --poll "${POLL_DURATION}" --for "${totalduration}" --busy --header=false | tee "$(pwd)/out/${filename}"
  else
    cstat --poll "${POLL_DURATION}" --for "${TOTAL_DURATION}" --busy --header=false | tee "$(pwd)/out/${filename}"
  fi
}


cleanup() {
  echo "  >> Deleting local clusters and Docker containers ..."
  out/minikube delete --all 2>/dev/null >/dev/null
  k3d cluster delete 2>/dev/null >/dev/null
  kind delete cluster 2>/dev/null >/dev/null
  docker stop $(docker ps -q) 2>/dev/null
  docker kill $(docker ps -q) 2>/dev/null
  docker rm $(docker ps -a -q) 2>/dev/null
  sleep 2
}

pause_if_running_apps() {
  while true; do
    local apps=$(osascript -e 'tell application "System Events" to get name of (processes where background only is false)'  | tr ',' '\n' | sed s/"^ "//g)
    local quiet=0

    for app in $apps; do
      quiet=1
      if [[ "${app}" != "Terminal" && "${app}" != "Finder" ]]; then
        echo "Unexpected application running: \"${app}\" - will sleep"
        quiet=0
      fi
    done

    pmset -g batt | grep 'AC Power'
    if [[ "$?" != 0 ]]; then
      echo "waiting to be plugged in ..."
      sleep 5
      continue
    fi

    if [[ "${quiet}" == 1 ]]; then
      break
    else
      echo "waiting for apps to be closed ..."
      sleep 5
    fi

  done
}

fail() {
  local name=$1
  local iteration=$2

  echo '***********************************************************************'
  echo "${name} failed on iteration ${iteration} - will not record measurement"
  echo '***********************************************************************'

  if [[ "${iteration}" == 0 ]]; then
    echo "test environment appears invalid, exiting"
    exit 90
  fi
}

start_docker() {
    local docker_up=0
    local started=0

    while [[ "${docker_up}" == 0 ]]; do
      docker info >/dev/null && docker_up=1 || docker_up=0

      if [[ "${docker_up}" == 0 && "${started}" == 0 ]]; then
          if [[ "${OS}" == "Darwin" ]]; then
            echo ""
            echo "  >> Starting Docker for Desktop ..."
            open -a Docker
            started=1
          elif [[ "${OS}" == "Linux" ]]; then
            echo ""
            echo "  >> Starting Docker Engine ..."
            sudo systemctl start docker
            started=1
          fi
      fi

      sleep 1
    done

    # Give time for d4d Kubernetes to begin, if it's around
    if [[ "${started}" == 1 ]]; then
      sleep 60
    fi
}


main() {
  # check if cstat is installed
  CSTAT=$(which cstat)
  if [[ "$?" != 0 ]]; then
    echo "cstat in not installed. Install cstat at https://github.com/tstromberg/cstat"
    exit 1
  fi

  echo "----[ versions ]------------------------------------"
  k3d version || { echo "k3d version failed. Please install latest k3d"; exit 1; }
  kind version || { echo "kind version failed. Please install latest kind"; exit 1; }
  out/minikube version || { echo "minikube version failed"; exit 1; }
  docker version
  echo "----------------------------------------------------"
  echo ""

  echo "Session ID: ${SESSION_ID}"
  mkdir -p "out/benchmark-results/${SESSION_ID}"

  echo ""

  if [[ "${OS}" == "Darwin" ]]; then
    echo "Turning on Wi-Fi for initial downloads"
    networksetup -setairportpower Wi-Fi on
  fi

  for i in $(seq 0 ${TEST_ITERATIONS}); do
    echo ""
    echo "==> session ${SESSION_ID}, iteration $i"

    cleanup

    if [[ "$i" = 0 ]]; then
      echo "NOTE: The 0 iteration is an unmeasured dry run!"
    else
      if [[ "${OS}" == "Darwin" ]]; then
        pause_if_running_apps
        echo "Turning off Wi-Fi to remove background noise"
        networksetup -setairportpower Wi-Fi off

        echo "  >> Killing Docker for Desktop ..."
        osascript -e 'quit app "Docker"'
      elif [[ "${OS}" == "Linux" ]]; then
        echo "  >> Killing Docker Engine ..."
        sudo systemctl stop docker
      fi

      # Measure the background noise on this system
      sleep 15
      measure idle $i
    fi

    # Run cleanup once we can assert that Docker is up
    start_docker
    cleanup

    docker_k8s=0
    # depending on whether Docker for Mac Kubernetes is enabled
    if [[ "${OS}" == "Darwin" ]]; then
      # wait kubernetes system pods for Docker for Mac, if it is enabled
      sleep 60
      kubectl --context docker-desktop version

      # measure Docker for Mac Kubernetes
      if  [[ $? == 0 ]]; then
        echo "Kubernetes is running in Docker for Desktop - adjusting tests"
        docker_k8s=1
        kubectl create deployment nginx --image=nginx:1.20.0
        measure docker_k8s $i
        echo "end of measurement for Docker for Desktop"
        kubectl delete deployment nginx
      # measure Docker idle
      else
        kubectl create deployment nginx --image=nginx:1.20.0
        measure docker $i
        echo "end of measurement for Docker for Desktop Kubernetes"
        kubectl delete deployment nginx
      fi
    # measure Docker idle only
    elif [[ "${OS}" == "Linux" ]]; then
      measure docker $i
    fi

    echo ""
    echo "-> k3d"
    time k3d cluster create
    echo "-> deploy nginx deployment"
    kubectl create deployment nginx --image=nginx:1.20.0
    measure k3d $i || fail k3d $i
    cleanup

    echo ""
    echo "-> kind"
    time kind create cluster
    echo "-> deploy nginx deployment"
    kubectl create deployment nginx --image=nginx:1.20.0
    measure kind $i || fail kind $i
    cleanup

    # test different drivers
    if [[ "${OS}" == "Darwin" ]]; then
      drivers=(docker hyperkit virtualbox)
    elif [[ "${OS}" == "Linux" ]]; then
      drivers=(docker kvm2 virtualbox)
    fi

    for driver in "${drivers[@]}"; do
      echo ""
      # 1. start minikube cluster
      echo "-> out/minikube --driver=${driver}"
      time out/minikube start --driver "${driver}"

      #2. deploy sample application(nginx deployment)
      echo "-> deploy nginx deployment"
      kubectl create deployment nginx --image=nginx:1.20.0

      #3. wait 1 minute without anything and 4. measure No.3 idle CPU usage
      measure "minikube.${driver}.nonautopause" $i "1m" || fail "minikube.${driver}.nonautopause" $i

      # 5. enable auto-pause addons
      echo "-> enable auto-pause to control plane"
      out/minikube addons enable auto-pause

      # 6. wait 1 minute so that control plane will become Paused status
      pause=0
      while [ "${pause}" = 0 ]
      do
	# It takes 1 minute to become Pause status from Stopped status. 70s is a number with a margin
	sleep 70
        # 7. verify if minikube control plane is paused
        PAUSE=$(out/minikube status)
        echo $PAUSE | grep "apiserver: Paused"
        if [[ "$?" == 0 ]]; then
          echo "kube-apiserver is paused"
	  pause=1
	else
	  echo "...status is not Paused. wait for becoming Pause..."
        fi
      done

      # 8. wait 3 minute without anything and 9. measure No.8 idle CPU usage
      measure "minikube.${driver}.autopause" $i "3m" || fail "minikube.${driver}.autopause" $i
      cleanup

      # We won't be needing docker for the remaining tests this iteration
      if [[ "${OS}" == "Darwin" && "${driver}" == "docker" ]]; then
        echo "  >> Quitting Docker for Desktop ..."
        osascript -e 'quit app "Docker"'
      elif [[ "${OS}" == "Linux" && ${driver} == "docker" ]]; then
        echo "  >> Quitting Docker Engine ..."
        sudo systemctl stop docker
      fi
    done ## driver
  done ## iteration
}

main "$@"
# update benchmark result into docs contents
./hack/benchmark/cpu_usage/auto_pause/update_summary.sh "${SESSION_ID}"
go run ./hack/benchmark/cpu_usage/auto_pause/chart.go "${SESSION_ID}"
