#!/bin/bash
# Copyright 2017 Mirantis
#
# 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.

set -o pipefail
set -o errtrace

apiserver_static_pod="/etc/kubernetes/manifests/kube-apiserver"

# jq filters follow.

# TODO: think about more secure possibilities
apiserver_anonymous_auth='.spec.containers[0].command|=map(select(startswith("--token-auth-file")|not))+["--token-auth-file=/etc/pki/tokens.csv"]'

# Sets etcd2 as backend
apiserver_etcd2_backend='.spec.containers[0].command|=map(select(startswith("--storage-backend")|not))+["--storage-backend=etcd2"]'

# Make apiserver accept insecure connections on port 8080
# TODO: don't use insecure port
#apiserver_insecure_bind_port='.spec.containers[0].command|=map(select(startswith("--insecure-port=")|not))+["--insecure-port=2375"]'


# Update kube-proxy CIDR, enable --masquerade-all and disable conntrack (see dind::frob-proxy below)
function dind::proxy-cidr-and-no-conntrack {
  cluster_cidr="$(ip addr show docker0 | grep -w inet | awk '{ print $2; }')"
  echo ".items[0].spec.template.spec.containers[0].command |= .+ [\"--cluster-cidr=${cluster_cidr}\", \"--masquerade-all\", \"--conntrack-max-per-core=0\"]"
}


# Adds route to defualt eth0 interface so 10.96.x.x can go through
function dind::add-route {
   ip route add 10.96.0.0/16 dev eth0
}



function dind::join-filters {
  local IFS="|"
  echo "$*"
}

function dind::frob-apiserver {
  local -a filters=("${apiserver_anonymous_auth}")

  dind::frob-file "${apiserver_static_pod}" "${filters[@]}"
}

function dind::frob-file {
  local path_base="$1"
  shift
  local filter="$(dind::join-filters "$@")"
  local status=0
  if [[ -f ${path_base}.yaml ]]; then
    dind::yq "${filter}" "${path_base}.yaml" || status=$?
  else
    echo "${path_base}.json or ${path_base}.yaml not found" >&2
    return 1
  fi
  if [[ ${status} -ne 0 ]]; then
    echo "Failed to frob ${path_base}.yaml" >&2
    return 1
  fi
}

function dind::yq {
  local filter="$1"
  local path="$2"
  # We need to use a temp file here because if you feed an object to
  # 'kubectl convert' via stdin, you'll get a List object because
  # multiple input objects are implied
  tmp="$(mktemp tmp-XXXXXXXXXX.json)"
  kubectl convert -f "${path}" --local -o json 2>/dev/null |
    jq "${filter}" > "${tmp}"
  kubectl convert -f "${tmp}" --local -o yaml 2>/dev/null >"${path}"
  rm -f "${tmp}"
}

function dind::frob-proxy {
  # Trying to change conntrack settings fails even in priveleged containers,
  # so we need to avoid it. Here's sample error message from kube-proxy:
  # I1010 21:53:00.525940       1 conntrack.go:57] Setting conntrack hashsize to 49152
  # Error: write /sys/module/nf_conntrack/parameters/hashsize: operation not supported
  # write /sys/module/nf_conntrack/parameters/hashsize: operation not supported
  #
  # Recipe by @errordeveloper:
  # https://github.com/kubernetes/kubernetes/pull/34522#issuecomment-253248985
  local force_apply=--force
  if ! kubectl version --short >&/dev/null; then
    # kubectl 1.4 doesn't have version --short and also it doesn't support apply --force
    force_apply=
  fi
  KUBECONFIG=/etc/kubernetes/admin.conf kubectl -n kube-system get ds -l k8s-app=kube-proxy -o json |
    jq "$(dind::join-filters "$(dind::proxy-cidr-and-no-conntrack)")" | KUBECONFIG=/etc/kubernetes/admin.conf kubectl apply ${force_apply} -f -

  KUBECONFIG=/etc/kubernetes/admin.conf kubectl -n kube-system delete pods --now -l "k8s-app=kube-proxy"
}


function dind::wait-for-apiserver {
    echo -n "Waiting for api server to startup"
    local url="https://localhost:6443/api"
    local n=60
    while true; do
      if curl -k -s "${url}" >&/dev/null; then
        break
      fi
      if ((--n == 0)); then
        echo "Error: timed out waiting for apiserver to become available" >&2
      fi
      echo -n "."
      sleep 0.5
    done
    echo ""
}

function dind::frob-cluster {
  #dind::frob-apiserver
  dind::wait-for-apiserver
  dind::frob-proxy
}

# Weave depends on /etc/machine-id being unique
if [[ ! -f /etc/machine-id ]]; then
  rm -f /etc/machine-id
  systemd-machine-id-setup
fi

if [[ "$@" == "init"* || "$@" == "join"* ]]; then
# Call kubeadm with params and skip flag
	/usr/bin/kubeadm "$@" --ignore-preflight-errors all --cri-socket /run/docker/containerd/containerd.sock
else
# Call kubeadm with params
	/usr/bin/kubeadm "$@"
fi

# Frob cluster
if [[ "$@" == "init"* && $? -eq 0 && ! "$@" == *"--help"* ]]; then
   dind::frob-cluster
else
   dind::add-route
fi

