Created
June 9, 2019 18:33
-
-
Save imduffy15/d0e5d8f77858d5c525819a0ee10e4d03 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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