From ab567f69502f8f134febe2f92626f395a26e2832 Mon Sep 17 00:00:00 2001 From: yage Date: Mon, 28 Aug 2023 18:42:38 -0600 Subject: [PATCH] v3.6.0 ===================================================================== --- New Features ------------------------- - Introducing an optional plugin for `kubectl` facilitation! Check out 'plugins/kubectl/README.md' for more details. --- Changes ------------------------------ - The function which lists all available scwrypts now ignores directories with a top-level base called "plugins." If this is a name conflict, you will need to define your own `SCWRYPTS__LIST_AVAILABLE_SCWRYPTS__` function! (ref the changes in 'zsh/lib/scwrypts/run.module.zsh') --- plugins/kubectl/.config/env.template | 2 + .../kubectl/.config/env.template.descriptions | 1 + plugins/kubectl/.config/static/redis.zsh | 4 + plugins/kubectl/README.md | 10 + plugins/kubectl/get-context | 16 ++ plugins/kubectl/get-namespace | 16 ++ plugins/kubectl/kubectl.driver.completion.zsh | 31 ++++ plugins/kubectl/kubectl.driver.zsh | 173 ++++++++++++++++++ plugins/kubectl/kubectl.scwrypts.zsh | 11 ++ plugins/kubectl/lib/kubectl.module.zsh | 79 ++++++++ plugins/kubectl/lib/redis.module.zsh | 90 +++++++++ .../kubectl/meta/get-static-redis-definition | 11 ++ plugins/kubectl/set-context | 28 +++ plugins/kubectl/set-namespace | 28 +++ zsh/lib/config.user.zsh | 5 + zsh/lib/config.zsh | 6 + zsh/lib/scwrypts/run.module.zsh | 1 + 17 files changed, 512 insertions(+) create mode 100644 plugins/kubectl/.config/env.template create mode 100644 plugins/kubectl/.config/env.template.descriptions create mode 100644 plugins/kubectl/.config/static/redis.zsh create mode 100644 plugins/kubectl/README.md create mode 100755 plugins/kubectl/get-context create mode 100755 plugins/kubectl/get-namespace create mode 100644 plugins/kubectl/kubectl.driver.completion.zsh create mode 100644 plugins/kubectl/kubectl.driver.zsh create mode 100644 plugins/kubectl/kubectl.scwrypts.zsh create mode 100644 plugins/kubectl/lib/kubectl.module.zsh create mode 100644 plugins/kubectl/lib/redis.module.zsh create mode 100755 plugins/kubectl/meta/get-static-redis-definition create mode 100755 plugins/kubectl/set-context create mode 100755 plugins/kubectl/set-namespace diff --git a/plugins/kubectl/.config/env.template b/plugins/kubectl/.config/env.template new file mode 100644 index 0000000..587f9c3 --- /dev/null +++ b/plugins/kubectl/.config/env.template @@ -0,0 +1,2 @@ +#!/bin/zsh +export SCWRYPTS_KUBECTL_REDIS= diff --git a/plugins/kubectl/.config/env.template.descriptions b/plugins/kubectl/.config/env.template.descriptions new file mode 100644 index 0000000..ef36939 --- /dev/null +++ b/plugins/kubectl/.config/env.template.descriptions @@ -0,0 +1 @@ +SCWRYPTS_KUBECTL_REDIS | (currently only 'managed') 'managed' or 'custom' redis configuration diff --git a/plugins/kubectl/.config/static/redis.zsh b/plugins/kubectl/.config/static/redis.zsh new file mode 100644 index 0000000..aea6ec8 --- /dev/null +++ b/plugins/kubectl/.config/static/redis.zsh @@ -0,0 +1,4 @@ +export SCWRYPTS_KUBECTL_REDIS_HOST__managed=127.0.0.1 +export SCWRYPTS_KUBECTL_REDIS_PORT__managed=26379 +export SCWRYPTS_KUBECTL_REDIS_AUTH__managed= +export SCWRYPTS_KUBECTL_REDIS_KEY_PREFIX__managed= diff --git a/plugins/kubectl/README.md b/plugins/kubectl/README.md new file mode 100644 index 0000000..8a40381 --- /dev/null +++ b/plugins/kubectl/README.md @@ -0,0 +1,10 @@ +# Kubernetes `kubectl` Helper Plugin + +Leverages a local `redis` application to quickly and easily set an alias `k` for `kubectl --context --namespace `. +Much like scwrypts environments, `k` aliases are *only* shared amongst session with the same `SCWRYPTS_ENV` to prevent accidental cross-contamination. + + +## Getting Started + +Enable the plugin in `~/.config/scwrypts/config.zsh` by adding `SCWRYPTS_PLUGIN_ENABLED__KUBECTL=1`. +Use `k` as your new `kubectl` and checkout `k --help` and `k meta --help`. diff --git a/plugins/kubectl/get-context b/plugins/kubectl/get-context new file mode 100755 index 0000000..926cd05 --- /dev/null +++ b/plugins/kubectl/get-context @@ -0,0 +1,16 @@ +#!/bin/zsh +##################################################################### +DEPENDENCIES+=() +REQUIRED_ENV+=() + +use kubectl --group kubectl + +CHECK_ENVIRONMENT +##################################################################### + +MAIN() { + KUBECTL__GET_CONTEXT +} + +##################################################################### +MAIN $@ diff --git a/plugins/kubectl/get-namespace b/plugins/kubectl/get-namespace new file mode 100755 index 0000000..c1a6a44 --- /dev/null +++ b/plugins/kubectl/get-namespace @@ -0,0 +1,16 @@ +#!/bin/zsh +##################################################################### +DEPENDENCIES+=() +REQUIRED_ENV+=() + +use kubectl --group kubectl + +CHECK_ENVIRONMENT +##################################################################### + +MAIN() { + KUBECTL__GET_NAMESPACE +} + +##################################################################### +MAIN $@ diff --git a/plugins/kubectl/kubectl.driver.completion.zsh b/plugins/kubectl/kubectl.driver.completion.zsh new file mode 100644 index 0000000..e99ddf5 --- /dev/null +++ b/plugins/kubectl/kubectl.driver.completion.zsh @@ -0,0 +1,31 @@ +##################################################################### +command -v compdef >/dev/null 2>&1 || return 0 +##################################################################### + +_k() { + local C=$(k meta get context) + local NS=$(k meta get namespace) + + local KUBEWORDS=(kubectl) + [ $C ] && KUBEWORDS+=(--context $C) + [ $NS ] && KUBEWORDS+=(--namespace $NS) + + words="$KUBEWORDS ${words[@]:1}" + _kubectl +} + +compdef _k k + +##################################################################### +_h() { + local C=$(k meta get context) + local NS=$(k meta get namespace) + + local KUBEWORDS=(kubectl) + [ $C ] && KUBEWORDS+=(--context $C) + [ $NS ] && KUBEWORDS+=(--namespace $NS) + + words="$KUBEWORDS ${words[@]:1}" + _helm +} +compdef _h h diff --git a/plugins/kubectl/kubectl.driver.zsh b/plugins/kubectl/kubectl.driver.zsh new file mode 100644 index 0000000..99c7128 --- /dev/null +++ b/plugins/kubectl/kubectl.driver.zsh @@ -0,0 +1,173 @@ +[[ $SCWRYPTS_KUBECTL_DRIVER_READY -eq 1 ]] && return 0 + +k() { _SCWRYPTS_KUBECTL_DRIVER kubectl $@; } +h() { _SCWRYPTS_KUBECTL_DRIVER helm $@; } + +_SCWRYPTS_KUBECTL_DRIVER() { + [ ! $SCWRYPTS_ENV ] && { + ERROR "must set SCWRYPTS_ENV in order to use '$(echo $CLI | head -c1)'" + return 1 + } + + which REDIS >/dev/null 2>&1 \ + || eval "$(scwrypts -n --name meta/get-static-redis-definition --type zsh --group kubectl)" + + local CLI="$1"; shift 1 + + local SCWRYPTS_GROUP CUSTOM_COMMANDS=(meta) + for SCWRYPTS_GROUP in ${SCWRYPTS_GROUPS[@]} + do + CUSTOM_COMMANDS+=($(eval echo '$SCWRYPTS_KUBECTL_CUSTOM_COMMANDS__'$SCWRYPTS_GROUP)) + done + + ########################################## + + local USAGE_ARGS="$(for C in ${CUSTOM_COMMANDS[@]}; do echo " - $C"; done)" + local USAGE_OPTIONS=" + -n, --namespace set the namespace for commands in '$SCWRYPTS_ENV' + -k, --context set the context for commands in '$SCWRYPTS_ENV' + " + local DESCRIPTION=" + Provides 'k' (kubectl) and 'h' (helm) shorthands to the + respective utility. These functions leverage redis and scwrypts + environments to allow quick selection of contexts and namespaces + usable across all active shell instances. + + The scwrypts group 'kubectl' has simple selection executables + for kubecontext and namespace, but also provides the library to + enable enriched, use-case-sensitive setup of kubernetes context. + " + + local USAGE=" + usage: $(echo $CLI | head -c1) [...args...] [...options...] -- [...$CLI options...] + + args: - + + options: - + -h, --help display this help dialogue + -v, --verbose output debugging information + + DESCRIPTION + " + + ########################################## + + local USER_ARGS=() + + local CUSTOM_COMMAND=0 + PARAMETER_OVERRIDES+= + local VERBOSE=0 + + while [[ $# -gt 0 ]] + do + case $1 in + -v | --verbose ) VERBOSE=1 ;; + -n | --namespace ) + echo "TODO: set namespace ('$2')" >&2 + USER_ARGS+=(--namespace $2); shift 1 + ;; + + -k | --context | --kube-context ) + echo "TODO: set context ('$2')" >&2 + [[ $CLI =~ ^helm$ ]] && USER_ARGS+=(--kube-context $2) + [[ $CLI =~ ^kubectl$ ]] && USER_ARGS+=(--context $2) + shift 1 + ;; + + meta ) + CUSTOM_COMMAND=meta + USAGE_ARGS=" - get\n - set" + USAGE_OPTIONS='' + DESCRIPTION="perform meta-operations on $(echo $CLI | head -c1) for '$SCWRYPTS_ENV'" + + case $2 in + -h | --help ) HELP=1 ;; + + set ) + USAGE_ARGS=" set (namespace|context)" + DESCRIPTION="interactively set a namespace or context for '$SCWRYPTS_ENV'" + case $3 in + namespace | context ) USER_ARGS+=($2 $3) ;; + -h | --help ) HELP=1 ;; + + * ) ERROR "cannot set '$3'" >&2 ;; + esac + shift 1 + ;; + + get ) + USAGE_ARGS=" get (namespace|context)" + DESCRIPTION="output the current namespace or context for '$SCWRYPTS_ENV'" + case $3 in + namespace | context ) USER_ARGS+=($2 $3) ;; + -h | --help ) HELP=1 ;; + + * ) ERROR "cannot get '$3'" >&2 ;; + esac + shift 1 + ;; + esac + shift 1 + ;; + + -h | --help ) HELP=1 ;; + -- ) shift 1; break ;; + * ) USER_ARGS+=($1) ;; + esac + shift 1 + done + + while [[ $# -gt 0 ]]; do USER_ARGS+=($1); shift 1; done + + + CHECK_ERRORS --no-fail 2>&1 | sed 's/scwrypts -- //' >&2 || return 1 + + [[ $HELP -eq 1 ]] && { + [[ ! $CUSTOM_COMMAND =~ ^0$ ]] \ + && USAGE=$(echo $USAGE | sed "s/[[]\\.\\.\\.args\\.\\.\\.[]]/$CUSTOM_COMMAND &/") + + USAGE=$(echo $USAGE | perl -pe " + s/args: -/args:\n$USAGE_ARGS/; + s^options: -^options:$USAGE_OPTIONS^; + s/DESCRIPTION/$DESCRIPTION/; + ") + + USAGE 2>&1 | sed 's/scwrypts -- //' >&2 + return 0 + } + + ##################################################################### + + case $CUSTOM_COMMAND in + 0 ) + local CLI_ARGS=() + + local CONTEXT=$(k meta get context) + local NAMESPACE=$(k meta get namespace) + + [ $CONTEXT ] && [[ $CLI =~ ^helm$ ]] && CLI_ARGS+=(--kube-context $CONTEXT) + [ $CONTEXT ] && [[ $CLI =~ ^kubectl$ ]] && CLI_ARGS+=(--context $CONTEXT) + + [ $NAMESPACE ] && CLI_ARGS+=(--namespace $NAMESPACE) + [[ $VERBOSE -eq 1 ]] && { + INFO " + using context '$CONTEXT' + using namespace '$NAMESPACE' + " + STATUS "running $CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}" + } + $CLI ${CLI_ARGS[@]} ${USER_ARGS[@]} + ;; + * ) eval 'SCWRYPTS_KUBECTL_CUSTOM_COMMAND__'$CUSTOM_COMMAND ${USER_ARGS[@]} ;; + esac +} + +SCWRYPTS_KUBECTL_CUSTOM_COMMAND__meta() { + case $1 in + get ) REDIS get --prefix current:$2; return 0 ;; + set ) scwrypts --name set-$2 --type zsh --group kubectl ;; + esac +} + +##################################################################### +source ${0:a:h}/kubectl.driver.completion.zsh diff --git a/plugins/kubectl/kubectl.scwrypts.zsh b/plugins/kubectl/kubectl.scwrypts.zsh new file mode 100644 index 0000000..fa91ef4 --- /dev/null +++ b/plugins/kubectl/kubectl.scwrypts.zsh @@ -0,0 +1,11 @@ +SCWRYPTS_GROUPS+=(kubectl) + +export SCWRYPTS_TYPE__kubectl=zsh +export SCWRYPTS_ROOT__kubectl="$SCWRYPTS_ROOT__scwrypts/plugins/kubectl" +export SCWRYPTS_COLOR__kubectl='\033[0;31m' + +SCWRYPTS_STATIC_CONFIG__kubectl+=( + "$SCWRYPTS_ROOT__kubectl/.config/static/redis.zsh" +) + +source "$SCWRYPTS_ROOT__kubectl/kubectl.driver.zsh" diff --git a/plugins/kubectl/lib/kubectl.module.zsh b/plugins/kubectl/lib/kubectl.module.zsh new file mode 100644 index 0000000..fd97a9d --- /dev/null +++ b/plugins/kubectl/lib/kubectl.module.zsh @@ -0,0 +1,79 @@ +##################################################################### + +DEPENDENCIES+=( + kubectl +) + +REQUIRED_ENV+=() + +use redis --group kubectl + +##################################################################### + +KUBECTL() { + local NAMESPACE=$(REDIS get --prefix "current:namespace") + local CONTEXT=$(KUBECTL__GET_CONTEXT) + + local KUBECTL_ARGS=() + [ $NAMESPACE ] && KUBECTL_ARGS+=(--namespace $NAMESPACE) + [ $CONTEXT ] && KUBECTL_ARGS+=(--context $CONTEXT) + + kubectl ${KUBECTL_ARGS[@]} $@ +} + + +##################################################################### + +KUBECTL__GET_CONTEXT() { REDIS get --prefix "current:context"; } + +KUBECTL__SET_CONTEXT() { + local CONTEXT=$1 + [ ! $CONTEXT ] && return 1 + + [[ $CONTEXT =~ default ]] && { + : \ + && REDIS del --prefix "current:context" \ + && KUBECTL__SET_NAMESPACE default \ + ; + return $? + } + + : \ + && REDIS set --prefix "current:context" "$CONTEXT" \ + && KUBECTL__SET_NAMESPACE default \ + ; +} + +KUBECTL__SELECT_CONTEXT() { + KUBECTL__LIST_CONTEXTS | FZF 'select a context' +} + +KUBECTL__LIST_CONTEXTS() { + echo default + KUBECTL config get-contexts -o name | sort +} + +##################################################################### + +KUBECTL__GET_NAMESPACE() { REDIS get --prefix "current:namespace"; } + +KUBECTL__SET_NAMESPACE() { + local NAMESPACE=$1 + [ ! $NAMESPACE ] && return 1 + + [[ $NAMESPACE =~ default ]] && { + REDIS del --prefix "current:namespace" + return $? + } + + REDIS set --prefix "current:namespace" "$NAMESPACE" +} + +KUBECTL__SELECT_NAMESPACE() { + KUBECTL__LIST_NAMESPACES | FZF 'select a namespace' +} + +KUBECTL__LIST_NAMESPACES() { + echo default + KUBECTL get namespaces -o name | sed 's/^namespace\///' | sort +} diff --git a/plugins/kubectl/lib/redis.module.zsh b/plugins/kubectl/lib/redis.module.zsh new file mode 100644 index 0000000..ccddfec --- /dev/null +++ b/plugins/kubectl/lib/redis.module.zsh @@ -0,0 +1,90 @@ +##################################################################### + +DEPENDENCIES+=( + redis-cli + docker +) + +# TODO; allow custom redis configuration +export SCWRYPTS_KUBECTL_REDIS=managed + +REQUIRED_ENV+=( + SCWRYPTS_KUBECTL_REDIS +) + +##################################################################### + +REDIS() { + [ ! $USAGE ] && local USAGE=" + usage: [...options...] + + options: + -p, --prefix apply dynamic prefix to the next command line argument + + --get-prefix output key prefix for current session + --get-static-definition output the static ZSH function definition for REDIS + + other arguments are passed through to redis-cli + " + local REDIS_ARGS=() USER_ARGS=() + local REDIS_PREFIX=$(eval echo '$SCWRYPTS_KUBECTL_REDIS_KEY_PREFIX__'$SCWRYPTS_KUBECTL_REDIS) + + local ECHO_STATIC_DEFINITION=0 + + while [[ $# -gt 0 ]] + do + case $1 in + -p | --prefix ) USER_ARGS+=("${SCWRYPTS_ENV}:${REDIS_PREFIX}$2"); shift 1 ;; + + --get-prefix ) echo $REDIS_PREFIX; return 0 ;; + --get-static-definition ) ECHO_STATIC_DEFINITION=1 ;; + + * ) USER_ARGS+=($1) ;; + esac + shift 1 + done + + local REDIS_HOST=$(eval echo '$SCWRYPTS_KUBECTL_REDIS_HOST__'$SCWRYPTS_KUBECTL_REDIS) + local REDIS_PORT=$(eval echo '$SCWRYPTS_KUBECTL_REDIS_PORT__'$SCWRYPTS_KUBECTL_REDIS) + local REDIS_AUTH=$(eval echo '$SCWRYPTS_KUBECTL_REDIS_AUTH__'$SCWRYPTS_KUBECTL_REDIS) + + [ $REDIS_HOST ] && REDIS_ARGS+=(-h $REDIS_HOST) + [ $REDIS_PORT ] && REDIS_ARGS+=(-p $REDIS_PORT) + [ $REDIS_AUTH ] && REDIS_ARGS+=(-a $REDIS_AUTH) + + REDIS_ARGS+=(--raw) + + [[ $ECHO_STATIC_DEFINITION -eq 1 ]] && { + echo "REDIS() {\ + local USER_ARGS=(); \ + while [[ \$# -gt 0 ]]; \ + do \ + case \$1 in + -p | --prefix ) USER_ARGS+=(\"\${SCWRYPTS_ENV}:${REDIS_PREFIX}\$2\"); shift 1 ;; \ + * ) USER_ARGS+=(\$1) ;; \ + esac; \ + shift 1; \ + done; \ + redis-cli ${REDIS_ARGS[@]} \${USER_ARGS[@]}; \ + }" | sed 's/\s\+/ /g' + return 0 + } + + redis-cli ${REDIS_ARGS[@]} ${USER_ARGS[@]} +} + + +REDIS ping | grep -qi pong || { + RPID=$(docker ps -a | grep scwrypts-kubectl-redis | awk '{print $1;}') + [ $RPID ] && STATUS 'refreshing redis instance' && docker rm -f $RPID + unset RPID + + docker run \ + --detach \ + --name scwrypts-kubectl-redis \ + --publish $SCWRYPTS_KUBECTL_REDIS_PORT__managed:6379 \ + redis >/dev/null 2>&1 + + STATUS 'awaiting redis connection' + until REDIS ping 2>/dev/null | grep -qi pong; do sleep 0.5; done +} diff --git a/plugins/kubectl/meta/get-static-redis-definition b/plugins/kubectl/meta/get-static-redis-definition new file mode 100755 index 0000000..6817fb3 --- /dev/null +++ b/plugins/kubectl/meta/get-static-redis-definition @@ -0,0 +1,11 @@ +#!/bin/zsh +##################################################################### +DEPENDENCIES+=() +REQUIRED_ENV+=() + +use redis --group kubectl + +CHECK_ENVIRONMENT +##################################################################### + +echo $(REDIS --get-static-definition) diff --git a/plugins/kubectl/set-context b/plugins/kubectl/set-context new file mode 100755 index 0000000..1921c59 --- /dev/null +++ b/plugins/kubectl/set-context @@ -0,0 +1,28 @@ +#!/bin/zsh +##################################################################### +DEPENDENCIES+=() +REQUIRED_ENV+=() + +use kubectl --group kubectl + +CHECK_ENVIRONMENT +##################################################################### + +MAIN() { + local USAGE=" + usage: [context] + + args: + context the full name of the kubeconfig context to set + " + local CONTEXT="$1" + [ $CONTEXT ] || $(KUBECTL__SELECT_CONTEXT) + [ $CONTEXT ] || ERROR 'must provide or select a valid kube context' + + CHECK_ERRORS + + KUBECTL__SET_CONTEXT $CONTEXT +} + +##################################################################### +MAIN $@ diff --git a/plugins/kubectl/set-namespace b/plugins/kubectl/set-namespace new file mode 100755 index 0000000..f5128bc --- /dev/null +++ b/plugins/kubectl/set-namespace @@ -0,0 +1,28 @@ +#!/bin/zsh +##################################################################### +DEPENDENCIES+=() +REQUIRED_ENV+=() + +use kubectl --group kubectl + +CHECK_ENVIRONMENT +##################################################################### + +MAIN() { + local USAGE=" + usage: [namespace] + + args: + namespace the full name of the namespace to set + " + local NAMESPACE="$1" + [ $NAMESPACE ] || $(KUBECTL__SELECT_NAMESPACE) + [ $NAMESPACE ] || ERROR 'must provide or select a valid namespace' + + CHECK_ERRORS + + KUBECTL__SET_NAMESPACE $NAMESPACE +} + +##################################################################### +MAIN $@ diff --git a/zsh/lib/config.user.zsh b/zsh/lib/config.user.zsh index 72c8c91..3f5831a 100644 --- a/zsh/lib/config.user.zsh +++ b/zsh/lib/config.user.zsh @@ -13,3 +13,8 @@ SCWRYPTS_ENV_SHORTCUT='' # CTRL + / SCWRYPTS_ENV_PATH="$SCWRYPTS_CONFIG_PATH/environments" SCWRYPTS_LOG_PATH="$SCWRYPTS_DATA_PATH/logs" SCWRYPTS_OUTPUT_PATH="$SCWRYPTS_DATA_PATH/output" + + +# disabled = 0; enabled = 1 + +SCWRYPTS_PLUGIN_ENABLED__kubectl=1 diff --git a/zsh/lib/config.zsh b/zsh/lib/config.zsh index ebeb82c..4e01ced 100644 --- a/zsh/lib/config.zsh +++ b/zsh/lib/config.zsh @@ -38,5 +38,11 @@ SCWRYPTS_GROUPS=($(echo $SCWRYPTS_GROUPS | sed 's/\s\+/\n/g' | sort -u)) source "$SCWRYPTS_ROOT/zsh/lib/config.group.zsh" \ || FAIL 69 'failed to set up scwrypts group; aborting' +##################################################################### + +[[ $SCWRYPTS_PLUGIN_ENABLED__kubectl -eq 1 ]] && { + source "$SCWRYPTS_ROOT/plugins/kubectl/kubectl.scwrypts.zsh" +} + ##################################################################### __SCWRYPT=1 # arbitrary; indicates currently inside a scwrypt diff --git a/zsh/lib/scwrypts/run.module.zsh b/zsh/lib/scwrypts/run.module.zsh index 648171d..3547f44 100644 --- a/zsh/lib/scwrypts/run.module.zsh +++ b/zsh/lib/scwrypts/run.module.zsh @@ -54,6 +54,7 @@ SCWRYPTS__LIST_AVAILABLE_SCWRYPTS__scwrypts() { | grep -v '\.git' \ | grep -v 'node_modules' \ | sed "s/^\\.\\///; s/\\.[^.]*$//; s/^/$GROUP_TYPE/" \ + | grep -v '^plugins/' \ ; }