#!/bin/sh
# Common functions

# Customizable environment variables:
# * $U7S_DEBUG: enable debug mode if set to "1"

# Environment variables set by this script:
# * $XDG_DATA_HOME: ~u7s-admin/.local/share if not set
# * $XDG_CONFIG_HOME: ~u7s-admin/.config if not set
# * $XDG_CACHE_HOME: ~u7s-admin/.cache if not set

USERNAME='u7s-admin'

set -eo pipefail

# logging utilities
debug_enabled() {
	: ${U7S_DEBUG=0}
	[[ $U7S_DEBUG == 1 ]] || [[ $U7S_DEBUG == true ]]
}

log_debug() {
	if debug_enabled; then
		echo -e "\e[102m\e[97m[DEBUG]\e[49m\e[39m $@" >&2
	fi
}

log_info() {
	echo -e "\e[104m\e[97m[INFO]\e[49m\e[39m $@" >&2
}

log_info_n() {
	echo -n -e "\e[104m\e[97m[INFO]\e[49m\e[39m $@" >&2
}

log_warning() {
	echo -e "\e[101m\e[97m[WARN]\e[49m\e[39m $@" >&2
}

log_error() {
	echo -e "\e[101m\e[97m[ERROR]\e[49m\e[39m $@" >&2
}

# nsenter utilities
nsenter_main() {
	: ${_U7S_NSENTER_CHILD=0}
	if [[ $_U7S_NSENTER_CHILD == 0 ]]; then
		_U7S_NSENTER_CHILD=1
		export _U7S_NSENTER_CHILD
		nsenter__nsenter_retry_loop
		rc=0
		nsenter__nsenter $@ || rc=$?
		exit $rc
	fi
}

nsenter__nsenter_retry_loop() {
	local max_trial=10
	log_info_n "Entering RootlessKit namespaces: "
	for ((i = 0; i < max_trial; i++)); do
		rc=0
		nsenter__nsenter echo OK 2>/dev/null || rc=$?
		if [[ rc -eq 0 ]]; then
			return 0
		fi
		echo -n .
		sleep 1
	done
	log_error "nsenter failed after ${max_trial} attempts, RootlessKit not running?"
	return 1
}

nsenter__nsenter() {
	local pidfile=$XDG_RUNTIME_DIR/usernetes/rootlesskit/child_pid
	if ! [[ -f $pidfile ]]; then
		return 1
	fi
	# workaround for https://github.com/rootless-containers/rootlesskit/issues/37
	# see the corresponding code in rootlesskit
	local pidreadyfile=$XDG_RUNTIME_DIR/usernetes/rootlesskit/_child_pid.u7s-ready
	if ! [[ -f $pidreadyfile ]]; then
		return 1
	fi
	if ! [[ $(cat $pidfile) -eq $(cat $pidreadyfile) ]]; then
		return 1
	fi
	export ROOTLESSKIT_STATE_DIR=$XDG_RUNTIME_DIR/usernetes/rootlesskit
	# TODO(AkihiroSuda): ping to $XDG_RUNTIME_DIR/usernetes/rootlesskit/api.sock
	nsenter --user --preserve-credential --mount --net --cgroup --pid --ipc --uts -t $(cat $pidfile) --wd=$PWD -- $@
}

setEnvsByYaml() {
	yamlFile=$1
	ifs=$IFS
	for assign in $(yq '.spec.containers[0].command' $yamlFile | grep -- '"--')
	do
	  if [ "${assign:0-1}" = ',' ]
	  then
			assign=${assign:3:-2}
		else
			assign=${assign:3:-1}
		fi
		IFS==
		set -- $assign
		IFS=$ifs
		var=$1
		value=$2
		IFS=-
		set -- $var
		IFS=$ifs
		varsh=$1
		shift
		for p
		do
		  varsh+="_$p"
		done
		echo "export $varsh=$value"
	done
}

# Применение маски сети к указанному адресу
maskAddr() {
	set +e
  addr=$1
  mask=$2
  if [ $mask -eq 0 -o $mask -gt 32 ]
  then
    echo "Неверная маска $mask" >&2
    exit
  fi
  let tail=32-$mask
  ifs=$IFS
  IFS=.
  set -- $addr
  IFS=$ifs
  if [ $# -ne 4 ]
  then
    echo "Неверный адрес $addr" >&2
    exit
  fi

  Val=0
  for val
  do
    let Val=$Val*256+$val
  done
  let "Val=$Val>>$tail"
  let "Val=$Val<<$tail"

  let "ret=$Val&255"
  for i in 1 2 3
  do
    let "Val=$Val>>8"
    let "val=$Val&255"
    ret="$val.$ret"
  done

  echo $ret
}

# Получение внешнего IP
getExtIP() {
  set -- $(ip r | grep default)
  export router=$3

  ip a | grep 'inet ' |
  (
  while read inet
  do
    set -- $inet
    AddrMask=$2
    ifs=$IFS
    IFS=/
    set -- $AddrMask
    IFS=$ifs
    addr=$1
    mask=$2
    maskedAddr=$(maskAddr $addr $mask)
    maskedRoute=$(maskAddr $router $mask)
    if [ "$maskedAddr" = "$maskedRoute" ]
    then
      echo $addr
      break
    fi
  done
  )
}

# Получение имени внешнего устройства
getExtDev() {
	export extIP=$1
	ifs=$IFS
	IFS=.
	set -- $extIP
	IFS=$ifs
	if [ $# -ne 4 ]
	then
		echo "Некорректный адрес '$extIP'" >&2
		exit
	fi
	ip a | grep $extIP |
	(
  while read inet
  do
    set -- $inet
    if [ "$1" != 'inet' ]
    then
      continue
    fi
    addr=$2
    l=${#extIP}
    if [ "${addr:0:$l}" = "$extIP" -a "${addr:$l:1}" == '/' ]
    then
      while [ $# -gt 1 ]; do shift; done
      echo $1
      break
    fi
  done
	)
}

# Обеспечить выделение статического кластерного адреса из диапазоны 10.96.0.1 - 10.96.255.254
# См. https://kubernetes.io/docs/concepts/services-networking/cluster-ip-allocation/ для Cidr 10.96.0.0/12
getStaticClusterIP() {
	extIP=$1
	serviceCidr=$2
	ifs=$IFS
	IFS=.
	set -- $extIP
	IFS=$ifs
	major=$3 minor=$4
	if [ "$major" -eq 0 ]
	then
		if [ "$minor" -eq 1 -o "$minor" -eq 2  -o "$minor" -eq 3 -o "$minor" -eq 10 ] # service kubernetes, default router for slirp4net, service kubedns,
		then
			let minor+=3
		fi
	fi
	ifs=$IFS
	IFS=.
	set -- $serviceCidr
	IFS=$ifs
	echo "${1}.${2}.${major}.${minor}"
}

getCidr() {
	serviceCidr=$1
	shift; shift
	IFS=/
	set -- $serviceCidr
	IFS=$ifs
	serviceCidr=$1
	serviceMask=$2
	IFS=.
	set -- $serviceCidr
	IFS=$ifs
	if [ $# -ne 4 ]
	then
		echo "Некорректный адрес --service-cidr '$serviceCidr'" >&1
		exit 1
	fi
	echo $serviceCidr $serviceMask
}

# Функция формирует ClusterIP как serviceCidr с 1-чкой в последнем кварте
getKubernetesClusterIP() {
	serviceCidr=$1
	ifs=$IFS
	IFS=.
	set -- $serviceCidr
	IFS=$ifs
	echo "${1}.${2}.${3}.1"
}

# Функция модифицирует файл /kubernetes/manifests/kube-apiserver.yaml запуска kube-apiserver
# для настройки аудита
function tuneAudit() {
	haveAudit=$(cat /etc/kubernetes/manifests/kube-apiserver.yaml   | yq  '[.spec.volumes[].hostPath][].path | select(. == "/etc/kubernetes/audit")')
	if [ -z "$haveAudit" ]
	then
		TMPFILE="/tmp/kube-api-server.$$"
		confFile="/etc/kubernetes/manifests/kube-apiserver.yaml"
		cat $confFile |
		yq -y  '.spec.containers[].command |= . +
		["--audit-policy-file=/etc/kubernetes/audit/policy.yaml"] +
		["--audit-log-path=/etc/kubernetes/audit/audit.log"] +
		["--audit-log-maxsize=500"] +
		["--audit-log-maxbackup=3"]
		' |
		yq -y  '.spec.containers[].volumeMounts |= . +
		[{ "mountPath": "/etc/kubernetes/audit", "name": "audit" }]
		' |
		yq -y '.spec.volumes |= . +
		[{ "hostPath": {"path": "/etc/kubernetes/audit" , "type": "DirectoryOrCreate" }, "name": "audit" }]
		' > $TMPFILE
		if [ -s $TMPFILE ]
		then
			mv $TMPFILE $confFile
		fi
	fi
}


# export XDG_{DATA,CONFIG,CACHE}_HOME
: ${XDG_DATA_HOME=~u7s-admin/.local/share}
: ${XDG_CONFIG_HOME=~u7s-admin/.config}
: ${XDG_CACHE_HOME=~u7s-admin/.cache}
envFile="${XDG_CONFIG_HOME}/usernetes/env"
export XDG_DATA_HOME XDG_CONFIG_HOME XDG_CACHE_HOME envFile
