Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@imduffy15
Created June 9, 2019 18:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imduffy15/d0e5d8f77858d5c525819a0ee10e4d03 to your computer and use it in GitHub Desktop.
Save imduffy15/d0e5d8f77858d5c525819a0ee10e4d03 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
#
# kubensenter(1) is a utility to start a shell on the host of
# a kubernetes pod
[[ -n $DEBUG ]] && set -x
set -eou pipefail
IFS=$'\n\t'
# When called, the process ends.
# Args:
# $1: The exit message (print to stderr)
# $2: The exit code (default is 1)
# if env var _PRINT_HELP is set to 'yes', the usage is print to stderr (prior to )
# Example:
# test -f "$_arg_infile" || _PRINT_HELP=yes die "Can't continue, have to supply file as an argument, got '$_arg_infile'" 4
die()
{
local _ret=$2
test -n "$_ret" || _ret=1
test "$_PRINT_HELP" = yes && print_help >&2
echo "$1" >&2
exit ${_ret}
}
# Function that evaluates whether a value passed to it begins by a character
# that is a short option of an argument the script knows about.
# This is required in order to support getopts-like short options grouping.
begins_with_short_option()
{
local first_option all_short_options
all_short_options='h'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# THE DEFAULTS INITIALIZATION - POSITIONALS
# The positional args array has to be reset before the parsing, because it may already be defined
# - for example if this script is sourced by an argbash-powered script.
_positionals=()
# THE DEFAULTS INITIALIZATION - OPTIONALS
# Function that prints general usage of the script.
# This is useful if users asks for it, or if there is an argument parsing error (unexpected / spurious arguments)
# and it makes sense to remind the user how the script is supposed to be called.
print_help ()
{
printf 'Usage: %s [-h|--help] <pod> <container>\n' "$0"
printf '\t%s\n' "<pod>: the name of the pod you want a shell on"
printf '\t%s\n' "<container>: the container you want a shell on"
printf '\t%s\n' "-h,--help: Prints help"
}
# The parsing of the command-line
parse_commandline ()
{
while test $# -gt 0
do
_key="$1"
case "$_key" in
# The help argurment doesn't accept a value,
# we expect the --help or -h, so we watch for them.
-h|--help)
print_help
exit 0
;;
# We support getopts-style short arguments clustering,
# so as -h doesn't accept value, other short options may be appended to it, so we watch for -h*.
# After stripping the leading -h from the argument, we have to make sure
# that the first character that follows coresponds to a short option.
-h*)
print_help
exit 0
;;
*)
_positionals+=("$1")
;;
esac
shift
done
}
# Check that we receive expected amount positional arguments.
# Return 0 if everything is OK, 1 if we have too little arguments
# and 2 if we have too much arguments
handle_passed_args_count ()
{
_required_args_string="'pod' and 'container'"
test ${#_positionals[@]} -ge 2 || _PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require exactly 2 (namely: $_required_args_string), but got only ${#_positionals[@]}." 1
test ${#_positionals[@]} -le 2 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect exactly 2 (namely: $_required_args_string), but got ${#_positionals[@]} (the last one was: '${_positionals[*]: -1}')." 1
}
# Take arguments that we have received, and save them in variables of given names.
# The 'eval' command is needed as the name of target variable is saved into another variable.
assign_positional_args ()
{
# We have an array of variables to which we want to save positional args values.
# This array is able to hold array elements as targets.
_positional_names=('_arg_pod' '_arg_container' )
for (( ii = 0; ii < ${#_positionals[@]}; ii++))
do
eval "${_positional_names[ii]}=\${_positionals[ii]}" || die "Error during argument parsing, possibly an Argbash bug." 1
done
}
# Now call all the functions defined above that are needed to get the job done
parse_commandline "$@"
handle_passed_args_count
assign_positional_args
pod_template() {
node="$1"
container_id="$2"
cat <<EOF
{
"spec": {
"hostPID": true,
"nodeName": "$node",
"containers": [
{
"name": "1",
"image": "alpine",
"command": [
"nsenter",
"-t",
"1",
"-m",
"-u",
"-n",
"-p",
"--",
"sh",
"-c"
],
"args": [
"CONTAINER_PID=\$(docker inspect ${container_id} --format '{{.State.Pid}}') /bin/bash"
],
"stdin": true,
"tty": true,
"securityContext": {
"privileged": true
}
}
]
}
}
EOF
}
main() {
if hash kubectl 2>/dev/null; then
KUBECTL=kubectl
elif hash kubectl.exe 2>/dev/null; then
KUBECTL=kubectl.exe
else
echo >&2 "kubectl is not installed"
exit 1
fi
node=$($KUBECTL get pod "$_arg_pod" -o go-template --template='{{.spec.nodeName}}')
container_id=$($KUBECTL get pod "$_arg_pod" -o go-template --template="{{range .status.containerStatuses}}{{ if eq .name \"$_arg_container\" }}{{.containerID}}{{end}}{{end}}" | sed -e 's/docker:\/\///g')
$KUBECTL run kubensenter --restart=Never --image ignored -ti --rm --overrides "$(pod_template "$node" "$container_id")"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment