#!/usr/bin/sh
set -e

kubectl=kubectl
version=1.11.0
generator=""
node=""
nodefaultctx=0
nodefaultns=0
container_cpu="${KUBECTL_NODE_SHELL_POD_CPU:-100m}"
container_memory="${KUBECTL_NODE_SHELL_POD_MEMORY:-256Mi}"
volumes="[]"
volume_mounts="[]"
x_mode=false
labels="${KUBECTL_NODE_SHELL_LABELS}"
pod_running_timeout="${KUBECTL_NODE_SHELL_POD_RUNNING_TIMEOUT:-1m}"
image_pull_secret_name="${KUBECTL_NODE_SHELL_IMAGE_PULL_SECRET_NAME}"
custom_image=""
use_ipc=true
use_mount=true
use_pid=true
use_net=true
use_uts=true

if [ -t 0 ]; then
  tty=true
else
  tty=false
fi
while [ $# -gt 0 ]; do
  key="$1"

  case $key in
  -v | --version)
    echo "kubectl-node-shell $version"
    exit 0
    ;;
  --context)
    nodefaultctx=1
    kubectl="$kubectl --context $2"
    shift
    shift
    ;;
  --kubecontext=*)
    nodefaultctx=1
    kubectl="$kubectl --context=${key##*=}"
    shift
    ;;
  --kubeconfig)
    kubectl="$kubectl --kubeconfig $2"
    shift
    shift
    ;;
  --kubeconfig=*)
    kubectl="$kubectl --kubeconfig=${key##*=}"
    shift
    ;;
  -n | --namespace)
    nodefaultns=1
    kubectl="$kubectl --namespace $2"
    shift
    shift
    ;;
  --namespace=*)
    nodefaultns=1
    kubectl="$kubectl --namespace=${key##*=}"
    shift
    ;;
  -m | --mount)
    volumes=$(echo "$volumes" | jq '. += [{"persistentVolumeClaim": {"claimName": "'"$2"'", "type": ""}, "name": "'"pvc"'"}]')
    volume_mounts=$(echo "$volume_mounts" | jq '. += [{"mountPath": "/opt-pvc", "name": "'"pvc"'"}]')
    shift
    shift
    ;;
  -x)
    x_mode=true
    volumes=$(echo "$volumes" | jq '. += [{"hostPath": {"path": "/", "type": ""}, "name": "host-root"}]')
    volume_mounts=$(echo "$volume_mounts" | jq '. += [{"mountPath":"/host","name":"host-root"}]')
    shift
    ;;
  --image)
    custom_image="$2"
    shift
    shift
    ;;
  --no-ipc)
    use_ipc=false
    shift
    ;;
  --no-mount)
    use_mount=false
    shift
    ;;
  --no-pid)
    use_pid=false
    shift
    ;;
  --no-net)
    use_net=false
    shift
    ;;
  --no-uts)
    use_uts=false
    shift
    ;;
  --)
    shift
    break
    ;;
  *)
    if [ -z "$node" ]; then
      node="${1#node/}"
      shift
    else
      echo "exactly one node required"
      exit 1
    fi
    ;;
  esac
done

if [ -z "$node" ]; then
  echo "Please specify node name"
  exit 1
fi

if [ -z "$KUBERNETES_PORT" ]; then
  # Set the default context and namespace to avoid situations where the user switch them during the build process
  [ "$nodefaultctx" = 1 ] || kubectl="$kubectl --context=$(${kubectl} config current-context)"
  [ "$nodefaultns" = 1 ] || kubectl="$kubectl --namespace=$(${kubectl} config view --minify --output 'jsonpath={.contexts..namespace}')"
fi

# Check the node and retrieve the node OS label
os="$($kubectl get node $node -o jsonpath="{.metadata.labels.kubernetes\.io/os}" || exit 1)"

# Set pod configuration per operating system
if [ "$os" = "windows" ]; then
  default_image="mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0"
  image="${custom_image:-${KUBECTL_NODE_SHELL_IMAGE_WINDOWS:-$default_image}}"
  name="pwsh"
  pod="${name}-$(env LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 6)"
  cmd_start='"cmd.exe", "/c", "powershell.exe", "-nol"'
  cmd_arg_prefix=', "-Command"'
  cmd_default=''
  security_context='{"privileged":true,"windowsOptions":{"hostProcess":true,"runAsUserName":"NT AUTHORITY\\SYSTEM"}}'
else # If the OS isn't windows, assume linux
  default_image="docker.io/library/alpine"
  image="${custom_image:-${KUBECTL_NODE_SHELL_IMAGE:-$default_image}}"
  name="nsenter"
  pod="${name}-$(env LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 6)"
  cmd_start='"nsenter", "--target", "1"'
  # , "--mount", "--uts", "--ipc", "--net", "--pid"
  if [ "$use_mount" = true ]; then
    cmd_start="${cmd_start}, \"--mount\""
  fi
  if [ "$use_uts" = true ]; then
    cmd_start="${cmd_start}, \"--uts\""
  fi
  if [ "$use_ipc" = true ]; then
    cmd_start="${cmd_start}, \"--ipc\""
  fi
  if [ "$use_net" = true ]; then
    cmd_start="${cmd_start}, \"--net\""
  fi
  if [ "$use_pid" = true ]; then
    cmd_start="${cmd_start}, \"--pid\""
  fi

  cmd_arg_prefix=', "--"'
  cmd_default=', "bash", "-l"'
  security_context='{"privileged":true}'
fi

# Build the container command
if [ $# -gt 0 ]; then
  if [ "$x_mode" = true ]; then
    cmd='['
  else
    cmd="[ $cmd_start $cmd_arg_prefix,"
  fi
  c=""
  while [ $# -gt 0 ]; do
    cmd="${cmd}${c} \"$(echo "$1" | \
      awk '{gsub(/["\\]/,"\\\\&");gsub(/\x1b/,"\\u001b");printf "%s",last;last=$0"\\n"} END{print $0}' \
    )\""
    c=,
    shift
  done
  cmd="$cmd ]"
else
  if [ "$x_mode" = true ]; then
    cmd='null'
  else
    cmd="[ $cmd_start $cmd_default ]"
  fi
fi

# test if resource specification is required
resources_json='"resources": {
          "limits":   { "cpu": "'${container_cpu}'", "memory": "'${container_memory}'" },
          "requests": { "cpu": "'${container_cpu}'", "memory": "'${container_memory}'" }
        }'
$kubectl run --image "$image" "$pod" --dry-run=server 2>&1 | grep -q 'failed quota' || resources_json='"resources": {}'

if [ -n "${image_pull_secret_name}" ]; then
  image_pull_secrets='[ { "name": "'${image_pull_secret_name}'" } ]'
else
  image_pull_secrets='null'
fi

overrides="$(
cat <<EOT
{
  "spec": {
    "nodeName": "$node",
    "hostPID": $use_pid,
    "hostIPC": $use_ipc,
    "hostNetwork": $use_net,
    "imagePullSecrets": $image_pull_secrets,
    "containers": [
      {
        "securityContext": $security_context,
        "image": "$image",
        "name": "$name",
        "stdin": true,
        "stdinOnce": true,
        "tty": $tty,
        "command": $cmd,
        $resources_json,
        "volumeMounts": $volume_mounts
      }
    ],
    "tolerations": [
      { "key": "CriticalAddonsOnly", "operator": "Exists" },
      { "effect": "NoExecute",       "operator": "Exists" }
    ],
    "volumes": $volumes
  }
}
EOT
)"
# Support Kubectl <1.18
m=$(kubectl version --client -o yaml | awk -F'[ :"]+' '$2 == "minor" {print $3+0}')
if [ "$m" -lt 18 ]; then
  generator="--generator=run-pod/v1"
fi

trap "EC=\$?; $kubectl delete pod --wait=false $pod >&2 || true; exit \$EC" EXIT INT TERM

echo "spawning \"$pod\" on \"$node\"" >&2
$kubectl run --image "$image" --restart=Never --overrides="$overrides" --labels="$labels" --pod-running-timeout="$pod_running_timeout" $([ "$tty" = true ] && echo -t) -i "$pod" $generator
