dotfiles/local-bin/.local/bin/run-on-pod
2025-04-21 12:07:24 -03:00

279 lines
7.8 KiB
Bash
Executable file

#!/bin/env bash
# ARG_OPTIONAL_BOOLEAN([confirm],[c],[Wether to confirm the action or not],[on])
# ARG_OPTIONAL_SINGLE([runner],[r],[Specify the runner],[])
# ARG_TYPE_GROUP_SET([commands],[COMMAND],[runner],[node, sh],[index])
# ARG_POSITIONAL_SINGLE([context],[The Kubernetes context])
# ARG_POSITIONAL_SINGLE([script],[The script to run])
# ARG_POSITIONAL_SINGLE([pod_name],[The pod name])
# ARG_HELP([Prints this help message])
# ARG_VERSION([1.0])
# ARG_DEFAULTS_POS([])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
die() {
local _ret="${2:-1}"
test "${_PRINT_HELP:-no}" = yes && print_help >&2
echo "$1" >&2
exit "${_ret}"
}
# validators
commands() {
local _allowed=("node" "sh") _seeking="$1" _idx=0
for element in "${_allowed[@]}"; do
test "$element" = "$_seeking" && { test "$3" = "idx" && echo "$_idx" || echo "$element"; } && return 0
_idx=$((_idx + 1))
done
die "Value '$_seeking' (of argument '$2') doesn't match the list of allowed values: 'node' and 'sh'" 4
}
begins_with_short_option() {
local first_option all_short_options='crhv'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# THE DEFAULTS INITIALIZATION - POSITIONALS
_positionals=()
_arg_context=
_arg_script=
_arg_pod_name=
# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_confirm="on"
_arg_runner="sh"
print_help() {
printf '%s\n' "Prints this help message"
printf 'Usage: %s [-c|--(no-)confirm] [-r|--runner <COMMAND>] [-h|--help] [-v|--version] <context> <script> <pod_name>\n' "$0"
printf '\t%s\n' "<context>: The Kubernetes context"
printf '\t%s\n' "<script>: The script to run"
printf '\t%s\n' "<pod_name>: The pod name"
printf '\t%s\n' "-c, --confirm, --no-confirm: Wether to confirm the action or not (on by default)"
printf '\t%s\n' "-r, --runner: Specify the runner. Can be one of: 'node' and 'sh' (sh)"
printf '\t%s\n' "-h, --help: Prints help"
printf '\t%s\n' "-v, --version: Prints version"
}
parse_commandline() {
_positionals_count=0
while test $# -gt 0; do
_key="$1"
case "$_key" in
-c | --no-confirm | --confirm)
_arg_confirm="on"
test "${1:0:5}" = "--no-" && _arg_confirm="off"
;;
-c*)
_arg_confirm="on"
_next="${_key##-c}"
if test -n "$_next" -a "$_next" != "$_key"; then
{ begins_with_short_option "$_next" && shift && set -- "-c" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-r | --runner)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_runner="$(commands "$2" "runner")" || exit 1
shift
;;
--runner=*)
_arg_runner="$(commands "${_key##--runner=}" "runner")" || exit 1
;;
-r*)
_arg_runner="$(commands "${_key##-r}" "runner")" || exit 1
;;
-h | --help)
print_help
exit 0
;;
-h*)
print_help
exit 0
;;
-v | --version)
1.0
exit 0
;;
-v*)
1.0
exit 0
;;
*)
_last_positional="$1"
_positionals+=("$_last_positional")
_positionals_count=$((_positionals_count + 1))
;;
esac
shift
done
}
handle_passed_args_count() {
local _required_args_string="'context', 'script' and 'pod_name'"
test "${_positionals_count}" -ge 3 || _PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require exactly 3 (namely: $_required_args_string), but got only ${_positionals_count}." 1
test "${_positionals_count}" -le 3 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect exactly 3 (namely: $_required_args_string), but got ${_positionals_count} (the last one was: '${_last_positional}')." 1
}
assign_positional_args() {
local _positional_name _shift_for=$1
_positional_names="_arg_context _arg_script _arg_pod_name "
shift "$_shift_for"
for _positional_name in ${_positional_names}; do
test $# -gt 0 || break
eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
shift
done
}
parse_commandline "$@"
handle_passed_args_count
assign_positional_args 1 "${_positionals[@]}"
# OTHER STUFF GENERATED BY Argbash
# Validation of values
_arg_runner_index="$(commands "$_arg_runner" "runner" idx)"
### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash
# ] <-- needed because of Argbash
# Color definitions
PURPLE='#c462fc'
RED='#f44336'
GREEN='#8bc34a'
GOLD='#ffd700'
# Initial settings
ORIGINAL_CONTEXT=""
# Function to print a styled message using gum
print_gum_message() {
local color="$1"
local message="$2"
echo -e $(gum style --bold --foreground="$color" -- "$message")
}
# Function to handle confirmation prompts
ask_confirmation() {
if [ "$_arg_confirm" = "on" ]; then
echo -e "$1"
CHOICE=$(gum choose "Yes" "No")
[ "$CHOICE" = "Yes" ] || exit 1
fi
}
# Function to list available Kubernetes contexts
list_contexts() {
kubectl config get-contexts -o name
}
# Function to validate the provided context
validate_context() {
local context="$1"
if ! kubectl config get-contexts -o name | grep -q "^${context}$"; then
print_gum_message "$RED" "Invalid context: $context"
echo "Available contexts:"
list_contexts
exit 1
fi
}
# Function to validate the existence of the script
validate_script_existence() {
local script="$1"
if [ ! -f "$script" ]; then
print_gum_message "$RED" "Script not found: $script"
exit 1
fi
if [ ! -r "$script" ]; then
print_gum_message "$RED" "Script is not readable: $script"
exit 1
fi
}
# Function to backup the current Kubernetes context
backup_current_context() {
ORIGINAL_CONTEXT=$(kubectl config current-context)
if [ -z "$ORIGINAL_CONTEXT" ]; then
echo "Failed to retrieve the current context"
exit 1
fi
}
# Function to restore the original Kubernetes context
restore_original_context() {
kubectl config use-context "$ORIGINAL_CONTEXT"
if [ $? -ne 0 ]; then
echo "Failed to restore the original context"
exit 1
fi
print_gum_message "$GREEN" "Restored original context: $ORIGINAL_CONTEXT"
}
# Function to set Kubernetes context and namespace
set_kubernetes_context() {
validate_context "$_arg_context"
kubectl config use-context "$_arg_context" || {
echo "Failed to set context"
exit 1
}
kubectl config set-context --current --namespace=oc-app || {
echo "Failed to set namespace"
exit 1
}
}
# Function to get the running pod name
get_running_pod() {
kubectl get pod --field-selector=status.phase==Running --selector app.kubernetes.io/name="$_arg_pod_name" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null
}
# Main script logic
main() {
print_gum_message "$GREEN" "Using \"$_arg_runner\" runner"
validate_context "$_arg_context"
# Validate script existence
validate_script_existence "$_arg_script"
if [ "$_arg_confirm" = "off" ]; then
title=$(gum style --bold --foreground="$PURPLE" -- "Running without confirmation in 5 seconds...Press CTRL+C to cancel")
echo -e "$title"
sleep 5 || exit 1
fi
# Backup the current Kubernetes context
backup_current_context
# Ensure context will be restored on exit
trap restore_original_context EXIT
ask_confirmation "Execute '\e[31m$_arg_script\e[0m' on context '\e[32m$_arg_context\e[0m' using pod '\e[32m$_arg_pod_name\e[0m'?"
set_kubernetes_context
print_gum_message "$GREEN" "Getting pod $_arg_pod_name..."
pod=$(get_running_pod)
if [ -z "$pod" ]; then
print_gum_message "$RED" "Pod not found"
exit 1
fi
print_gum_message "$GREEN" "Found pod $pod"
ask_confirmation "Confirm execution of '\e[31m$_arg_script\e[0m' on pod '\e[32m$pod\e[0m'?"
print_gum_message "$GOLD" "Executing $_arg_script inside $pod..."
kubectl exec -i "$pod" -- "$_arg_runner" <"$_arg_script"
}
main "$@"