From ae3b6ebd4e27660951cb15c05e9020571e8d090e Mon Sep 17 00:00:00 2001 From: Manuel Vergara Date: Wed, 25 Jan 2023 15:57:30 +0100 Subject: [PATCH] Update with kubeselect script Signed-off-by: Manuel Vergara --- README.md | 3 +- src/kubeselect | 348 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+), 2 deletions(-) create mode 100755 src/kubeselect diff --git a/README.md b/README.md index 8ff9275..f2dcef8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -

NO PUBLICAR

- # configuraciones Ubuntu - ConfiguraciĆ³n Wifi en HP Pavilion. [WifiES.txt](doc/WifiES.txt) - Script actualizaciones de paquetes apt y snap. [actualizar.sh](src/actualizar.sh) - Script de pruebas de red. [pruebaRed.sh](src/pruebaRed.sh) +- Script para seleccionar contextos con kubectl [kubeselect](src/kubeselect) diff --git a/src/kubeselect b/src/kubeselect new file mode 100755 index 0000000..5d8db37 --- /dev/null +++ b/src/kubeselect @@ -0,0 +1,348 @@ +#!/usr/bin/env bash + +# This small helper util helps select an available Kubernetes context using the arrow keys +# using one simple command rather than two seperate kubectl commands to first show, then select contexts. +# It uses the mapfile shim found here: https://github.com/dosentmatter/bash-mapfile-shim +# which itself uses upvars by Freddy Vulto: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference +# These are only needed for bash versions < 4, otherwise the native functions are used. +# +# Finally, the small select_option function was found on this Stackoverflow thread: +# https://stackoverflow.com/questions/11426529/reading-output-of-a-command-into-an-array-in-bash +# I included all source code I used as is. + +# Bash: Passing variables by reference +# Copyright (C) 2010 Freddy Vulto +# Version: upvars-0.9.dev +# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# Assign variable one scope above the caller +# Usage: local "$1" && upvar $1 "value(s)" +# Param: $1 Variable name to assign value to +# Param: $* Value(s) to assign. If multiple values, an array is +# assigned, otherwise a single value is assigned. +# NOTE: For assigning multiple variables, use 'upvars'. Do NOT +# use multiple 'upvar' calls, since one 'upvar' call might +# reassign a variable to be used by another 'upvar' call. +# Example: +# +# f() { local b; g b; echo $b; } +# g() { local "$1" && upvar $1 bar; } +# f # Ok: b=bar +# +upvar() { + if unset -v "$1"; then # Unset & validate varname + if (( $# == 2 )); then + eval $1=\"\$2\" # Return single value + else + eval $1=\(\"\${@:2}\"\) # Return array + fi + fi +} + + +# Assign variables one scope above the caller +# Usage: local varname [varname ...] && +# upvars [-v varname value] | [-aN varname [value ...]] ... +# Available OPTIONS: +# -aN Assign next N values to varname as array +# -v Assign single value to varname +# Return: 1 if error occurs +# Example: +# +# f() { local a b; g a b; declare -p a b; } +# g() { +# local c=( foo bar ) +# local "$1" "$2" && upvars -v $1 A -a${#c[@]} $2 "${c[@]}" +# } +# f # Ok: a=A, b=(foo bar) +# +upvars() { + if ! (( $# )); then + echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\ + "value] | [-aN varname [value ...]] ..." 1>&2 + return 2 + fi + while (( $# )); do + case $1 in + -a*) + # Error checking + [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\ + "number specifier" 1>&2; return 1; } + printf %d "${1#-a}" &> /dev/null || { echo "bash:"\ + "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2 + return 1; } + # Assign array of -aN elements + [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) && + shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\ + "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; } + ;; + -v) + # Assign single value + [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" && + shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\ + "argument(s)" 1>&2; return 1; } + ;; + --help) echo "\ +Usage: local varname [varname ...] && + ${FUNCNAME[0]} [-v varname value] | [-aN varname [value ...]] ... +Available OPTIONS: +-aN VARNAME [value ...] assign next N values to varname as array +-v VARNAME value assign single value to varname +--help display this help and exit +--version output version information and exit" + return 0 ;; + --version) echo "\ +${FUNCNAME[0]}-0.9.dev +Copyright (C) 2010 Freddy Vulto +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law." + return 0 ;; + *) + echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2 + return 1 ;; + esac + done +} + +# use mapfile_function if mapfile not found +mapfile() { + if type -f mapfile &>/dev/null; then + command mapfile "$@" + else + mapfile_function "$@" + fi +} + +# Behaves like bash 4.x mapfile. +mapfile_function() { + local DELIM=$'\n' + local REMOVE_TRAILING_DELIM='false' + local OUTPUT_ARRAY_NAME='MAPFILE' + + local OPTIND OPTARG OPTERR + local option + OPTERR=0 + while getopts ':d:n:O:s:tu:C:c:' option; do + case "$option" in + d) + DELIM=$OPTARG + ;; + t) + REMOVE_TRAILING_DELIM='true' + ;; + :) + echo "Option requires an argument: -$OPTARG" >&2 + return 1 + ;; + \?) + echo "Illegal option: -$OPTARG" >&2 + return 2 + ;; + ?) + echo "Option unimplemented: -$option" >&2 + return 3 + ;; + esac + done + + shift "$((OPTIND - 1))" + + if (($# >= 1)); then + OUTPUT_ARRAY_NAME=$1 + fi + + local output_array=() + local null_end='false' + local REPLY + if [[ -n "$DELIM" ]]; then # stops at first null when DELIM is not null + IFS= read -r -d '' && null_end='true' + fi + while IFS= read -r -d "$DELIM"; do + if [[ "$REMOVE_TRAILING_DELIM" = 'true' ]]; then + output_array+=("$REPLY") + else + output_array+=("$REPLY$DELIM") + fi + done < <( + if [[ -n "$DELIM" ]]; then + echo -n "$REPLY" + [[ "$null_end" = 'true' ]] && printf '%b' '\0' + else + cat + fi + ) + if [[ -n "$REPLY" ]] || [[ "$null_end" = 'true' ]]; then + output_array+=("$REPLY") + fi + + local "$OUTPUT_ARRAY_NAME" && + upvars -a"${#output_array[@]}" "$OUTPUT_ARRAY_NAME" "${output_array[@]}" +} + +# mapfile according to word splitting rules. +# This isn't used by the shim. +# Empty lines aren't included in array if +# DELIM is whitespace (space, tab, newline) or null. +mapfile_IFS() { + local DELIM=$'\n' + local REMOVE_TRAILING_DELIM='false' + local OUTPUT_ARRAY_NAME='MAPFILE' + + local OPTIND OPTARG OPTERR + local option + OPTERR=0 + while getopts ':d:n:O:s:tu:C:c:' option; do + case "$option" in + d) + DELIM=$OPTARG + ;; + t) + REMOVE_TRAILING_DELIM='true' + ;; + :) + echo "Option requires an argument: -$OPTARG" >&2 + return 1 + ;; + \?) + echo "Illegal option: -$OPTARG" >&2 + return 2 + ;; + ?) + echo "Option unimplemented: -$option" >&2 + return 3 + ;; + esac + done + + shift "$((OPTIND - 1))" + + if (($# >= 1)); then + OUTPUT_ARRAY_NAME=$1 + fi + + local output_array=() + local null_end='false' + local delim_end='false' + local REPLY + local array_front + local array_last + if [[ -n "$DELIM" ]]; then + IFS= read -r -d '' && null_end='true' + [[ "$REPLY" = *"$DELIM" ]] && delim_end='true' + IFS=$DELIM read -r -d '' -a 'output_array' < <( + echo -n "$REPLY" + [[ "$null_end" = 'true' ]] && printf '%b' '\0' + ) + + if [[ "$REMOVE_TRAILING_DELIM" = 'false' ]]; then + array_front=("${output_array[@]:0:${#output_array[@]} - 1}") + array_last=${output_array[${#output_array[@]} - 1]} + + array_front=("${array_front[@]/%/$DELIM}") + if [[ "$delim_end" = 'true' ]]; then + array_last+=$DELIM + fi + + output_array=("${array_front[@]}" "$array_last") + fi + else + while IFS=$DELIM read -r -d ''; do + # no need to append null trailing delim because vars can't hold null + [[ -n "$REPLY" ]] && output_array+=("$REPLY") + done + [[ -n "$REPLY" ]] && output_array+=("$REPLY") + fi + + local "$OUTPUT_ARRAY_NAME" && + upvars -a"${#output_array[@]}" "$OUTPUT_ARRAY_NAME" "${output_array[@]}" +} + + +# Renders a text based list of options that can be selected by the +# user using up, down and enter keys and returns the chosen option. +# +# Arguments : list of options, maximum of 256 +# "opt1" "opt2" ... +# Return value: selected index (0 for opt1, 1 for opt2 ...) +function select_option { + + # little helpers for terminal print control and key input + ESC=$( printf "\033") + cursor_blink_on() { printf "$ESC[?25h"; } + cursor_blink_off() { printf "$ESC[?25l"; } + cursor_to() { printf "$ESC[$1;${2:-1}H"; } + print_option() { printf " $1 "; } + print_selected() { printf " $ESC[7m $1 $ESC[27m"; } + get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; } + key_input() { read -s -n3 key 2>/dev/null >&2 + if [[ $key = $ESC[A ]]; then echo up; fi + if [[ $key = $ESC[B ]]; then echo down; fi + if [[ $key = "" ]]; then echo enter; fi; } + + # initially print empty new lines (scroll down if at bottom of screen) + for opt; do printf "\n"; done + + # determine current screen position for overwriting the options + local lastrow=`get_cursor_row` + local startrow=$(($lastrow - $#)) + + # ensure cursor and input echoing back on upon a ctrl+c during read -s + trap "cursor_blink_on; stty echo; printf '\n'; exit" 2 + cursor_blink_off + + local selected=0 + while true; do + # print options by overwriting the last lines + local idx=0 + for opt; do + cursor_to $(($startrow + $idx)) + if [ $idx -eq $selected ]; then + print_selected "$opt" + else + print_option "$opt" + fi + ((idx++)) + done + + # user key control + case `key_input` in + enter) break;; + up) ((selected--)); + if [ $selected -lt 0 ]; then selected=$(($# - 1)); fi;; + down) ((selected++)); + if [ $selected -ge $# ]; then selected=0; fi;; + esac + done + + # cursor position back to normal + cursor_to $lastrow + printf "\n" + cursor_blink_on + + return $selected +} + +mapfile -t available_contexts < <( kubectl config get-contexts -oname ) + +echo "Select context using up/down keys and enter to confirm:" +echo + +select_option "${available_contexts[@]}" +choice=$? + +kubectl config use-context ${available_contexts[$choice]}