Last active
June 3, 2024 23:01
-
-
Save augustohp/deebc619c515463b9df6f46d8c7376af to your computer and use it in GitHub Desktop.
Execute a shell inside a running pod in kubernetes, uses fuzze search to traverse among available namespaces and pods.
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 sh | |
# | |
# Allows you to execute a shell into a running pod inside k8s. You can | |
# execute this script/command without any argument and it will display | |
# all available namespaces, then all available pods in the chosen namespace | |
# and then enter the pod you chose. | |
# | |
# Choices are given using fzf, allowing you to fuzzy search among occurrences. | |
# | |
# # Author: Augusto Pascutti <augusto.hp+oss@gmail> | |
# License: MIT | |
# URL: https://gist.github.com/augustohp/deebc619c515463b9df6f46d8c7376af | |
# vim: set ft=sh ts=4 sw=4 tw=0 noet: | |
# shellcheck disable=SC3043 | |
# TODO: Spawn new pod from deploysments options (-d) instead of using running pod | |
APP_NAME=$(basename "$0") | |
APP_VERSION="1.0.0" | |
APP_AUTHOR="augusto.hp+oss@gmail.com" | |
APP_DEPENDENCIES="kubectl awk fzf tr sort" | |
OPTION_NAMESPACE="" | |
OPTION_POD="" | |
OPTION_CONTAINER="" | |
OPTION_SHELL="/bin/sh" | |
set -e # Stops on failure | |
trap cleanup INT TERM EXIT | |
# Usage: cleanup | |
cleanup() | |
{ | |
exit 1 | |
} | |
# Usage: assert_env | |
assert_env() | |
{ | |
for dependency in $APP_DEPENDENCIES | |
do | |
if [ -n "$(command -v "$dependency")" ] | |
then | |
continue | |
fi | |
err "Missing dependency '$dependency', please install it." | |
exit 2 | |
done | |
} | |
# Usage: if empty "" && echo "empty" | |
empty() | |
{ | |
local value="$1" | |
if [ -z "$value" ] | |
then | |
return 0 | |
fi | |
return 1 | |
} | |
# Usage: kubectl get ns | strip_k8s_header | |
strip_k8s_header() | |
{ | |
grep -v "NAME" | |
} | |
# Usage: kubectl get ns | only_column 1 | |
only_column() | |
{ | |
local column="$1" | |
awk "{ print \$${column} }" | |
} | |
change_space_to_line() | |
{ | |
tr -s '[:space:]' '\n' | |
} | |
# ----------------------------------------------------------------------------- | |
# Context | |
# Filters the input with fuzzy search, the output is the line selected. | |
# Usage: cat /etc/hosts | fzf_menu [prompt] | |
fzf_menu() | |
{ | |
local header="Choose an option below (with arrow keys and ENTER):" | |
fzf --header="$header" \ | |
--height=35% \ | |
--reverse | |
} | |
# Usage: namespace="$(choose_namespace)" | |
choose_namespace() | |
{ | |
kubectl get namespaces \ | |
| strip_k8s_header \ | |
| only_column 1 \ | |
| fzf_menu "Choose a namespace" | |
} | |
# Usage: pod="$(choose_pod <namespace>)" | |
choose_pod() | |
{ | |
local namespace="$1" | |
kubectl get pods --namespace "$namespace" \ | |
| strip_k8s_header \ | |
| only_column 1 \ | |
| fzf_menu "Choose pod" | |
} | |
# Usage: container="$(choose_container <namespace> <pod>)" | |
choose_container() | |
{ | |
local namespace="$1" | |
local pod="$2" | |
kubectl get pods --namespace "$namespace" "$pod" -o jsonpath="{.spec.containers[*].name}" \ | |
| change_space_to_line \ | |
| sort \ | |
| fzf_menu "Choose container" | |
} | |
# Usage: enter_shell <namespace> <pod> <shell> | |
enter_shell() | |
{ | |
local namespace="$1" | |
local pod="$2" | |
local container="$3" | |
local shell="$4" | |
kubectl exec -n "$namespace" -c "$container" --stdin --tty "$pod" -- "$shell" | |
} | |
# Usage: pod_state <namespace> <pod> | |
pod_state() | |
{ | |
local namespace="$1" | |
local pod="$2" | |
kubectl get pod -n "$namespace" "$pod" 2> /dev/null \ | |
| strip_k8s_header \ | |
| only_column 3 | |
} | |
# Usage: if pod_not_running <namespace> <pod> && echo "NOT running" | |
pod_not_running() | |
{ | |
local namespace="$1" | |
local pod="$2" | |
state="$(pod_state "$namespace" "$pod")" | |
if [ "$state" = "Running" ] | |
then | |
return 1 | |
fi | |
return 0 | |
} | |
# ----------------------------------------------------------------------------- | |
# Execution | |
# Usage: display_help | |
display_help() | |
{ | |
cat <<-EOF | |
Usage: $APP_NAME [options] | |
$APP_NAME --bash --pod "my-app" --namespace "default" | |
Helps you execute a shell inside a running pod, using fzf to help | |
you choose among existing options (namespaces, pods). | |
fzf allows you to fuzzy search among lists of namespaces or options | |
and is a pre-requisite for this script to work. | |
Options: | |
-h, --help This message. | |
-v, --version Version information. | |
-x, --debug Displays debug information using -x to | |
execute this script. Prints line by line | |
instructions. | |
--bash, --sh What shell/binary to execute. | |
-p, --pod <pod> Name of the pod to execute shell. | |
-n, --namespace <name> Namespace the pod is into. If none | |
is passed, fzf is used to choose among | |
available. | |
-c, --container <name> Name of the container to execute commands | |
inside. If none is given, fzf is used to | |
choose among available ones. | |
Bugs and/or suggestions to $APP_AUTHOR. | |
EOF | |
} | |
# Usage: main "#@" | |
main() | |
{ | |
while : | |
do | |
case "${1:-}" in | |
-h | --help) | |
display_help | |
exit 1 | |
;; | |
-v | --version) | |
echo "$APP_VERSION" | |
exit 1 | |
;; | |
-x | --debug) | |
set -x | |
;; | |
-n | -ns | --ns | --namespace) | |
OPTION_NAMESPACE="$2" | |
shift | |
;; | |
-c | --container) | |
OPTION_CONTAINER="$2" | |
shift | |
;; | |
--sh) | |
OPTION_SHELL="/bin/sh" | |
;; | |
--bash) | |
OPTION_SHELL="/bin/bash" | |
;; | |
-p | --pod) | |
OPTION_POD="$2" | |
shift | |
;; | |
-?*) | |
echo "Error: Unknown option '$1'." >&2 | |
exit 2 | |
;; | |
*) | |
break | |
;; | |
esac | |
shift | |
done | |
# No namespace given, choose one | |
if empty "$OPTION_NAMESPACE" | |
then | |
OPTION_NAMESPACE="$(choose_namespace)" | |
fi | |
# No pod given, choose one | |
if empty "$OPTION_POD" | |
then | |
OPTION_POD="$(choose_pod "$OPTION_NAMESPACE")" | |
fi | |
if pod_not_running "$OPTION_NAMESPACE" "$OPTION_POD" | |
then | |
echo "Error: '$OPTION_POD' is not running." >&2 | |
exit 3 | |
fi | |
if empty "$OPTION_CONTAINER" | |
then | |
OPTION_CONTAINER="$(choose_container "$OPTION_NAMESPACE" "$OPTION_POD")" | |
fi | |
enter_shell "$OPTION_NAMESPACE" "$OPTION_POD" "$OPTION_CONTAINER" "$OPTION_SHELL" | |
return 0 | |
} | |
assert_env | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment