Skip to content

Instantly share code, notes, and snippets.

@washtubs
Last active April 24, 2022 22:59
Show Gist options
  • Save washtubs/616a16f2a0039e2168cfc2007cde3c8b to your computer and use it in GitHub Desktop.
Save washtubs/616a16f2a0039e2168cfc2007cde3c8b to your computer and use it in GitHub Desktop.
kubectl namespace completion for bash and zsh
# The result of putting this snippet in your bashrc is working completion
# for kubernetes aliases of the following form:
# + k == kubectl
# + kfoo == kubectl -n foo (where foo is a namespace on your cluster)
source <(kubectl completion bash)
alias k=kubectl
complete -F __start_kubectl k
function define_kubectl_alias() {
alias=$1 # e.g. kfoo
ns=$2 # e.g. foo
alias $alias="kubectl -n $ns"
# Normally you call this here:
# complete -F __start_kubectl kfoo
# Unfortunately, when kubectl dispatches off to get a list of pods in a situation like
# % kfoo get pod <TAB>
# ...Your alias buries the -n $ns, so it will try to find pods in the default namespace
# which might just be empty.
#
# So we dynamically create a function with eval
# to delegate off to __start_kubectl after modifying *ALL* bash completion input variables.
# Documented here: https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
# For the following explanation consider
# an *alias* would be "kfoo",
# and the *alias content* would be "kubectl -n foo"
#
# For the COMP_LINE we replace the alias with the alias content:
# kfoo becomes kubectl -n foo
#
# For the COMP_CWORD we add the difference in words between just the alias
# and the alias content since the current word is after substitution, that many words ahead,
# which is normally just 2 unless you changed the content
#
# For the COMP_WORDS we replace the first word with the 3 from the alias content
#
# For the COMP_POINT we add the difference in characters between the content and the alias
fn=__start_kubectl_$ns
definition='
function '$fn'() {
local ns='$ns'
local alias='$alias'
# This can be whatever you want as long as it is simple. Below logic is quite generic.
local alias_content="kubectl -n $ns"
local alias_words=( $alias_content )
local alias_content_words=$(printf %s $alias_content | wc -w)
local new_comp_line=$(printf %s "$COMP_LINE" | sed "s/^$alias/$alias_content/")
COMP_LINE=$new_comp_line
new_comp_words=()
for i in $(seq 0 $((${#alias_words[@]}-1))); do
new_comp_words+=( ${alias_words[$i]} )
done
for i in $(seq 1 $((${#COMP_WORDS[@]}-1))); do
new_comp_words+=( ${COMP_WORDS[$i]} )
done
COMP_WORDS=( "${new_comp_words[@]}" )
COMP_CWORD=$((COMP_CWORD + ${#alias_words[@]} - 1))
COMP_POINT=$((COMP_POINT + ${#alias_content} - ${#alias}))
__start_kubectl \"$@\"
}'
eval "$definition"
complete -F $fn $alias
}
# This part is optional, feel free to just use the function above to define your own aliases
#
# Loop through each namespace on the cluster and create an alias dynamically
# Note k8 namespaces will be passed to eval but there is no risk of anything malicious from that.
# They are limited to 63 characters, alphanumeric plus hyphens.
for ns in $(kubectl get ns --no-headers | awk '{print $1}')
do
define_kubectl_alias k$ns $ns
done
# The result of putting this snippet in your bashrc is working completion
# for kubernetes aliases of the following form:
# + k == kubectl
# + kfoo == kubectl -n foo (where foo is a namespace on your cluster)
alias k=kubectl
if [ $(zsh --version | awk '{print $2}') = "5.0.2" ]; then
# Bug in zsh 5.0.2.
#
# local tab=$(printf '\t')
#
# Makes an empty string in that version specifically,
# so we just patch what `kubectl completion zsh` gives us by throwing quotes around it.
source <(kubectl completion zsh | sed 's/local tab=$(printf .\\t.)/local tab="$(printf '"'"'\\t'"'"')"/')
else
source <(kubectl completion zsh)
fi
# This part is optional, feel free to just use the function above to define your own aliases
#
# Loop through each namespace on the cluster and create an alias dynamically
for ns in $(kubectl get ns --no-headers | awk '{print $1}')
do
alias k$ns="kubectl -n $ns"
done
@washtubs
Copy link
Author

washtubs commented Apr 24, 2022

Note these are just snippets meant for copying. Spending startup time scanning kubectl namespaces every time your shell inits may or may not be appropriate depending on your use case. There are a couple machines I exclusively do kubectl on, so it makes sense there.

Also feel free to drop the handling for zsh 5.0.2 which is currently what I'm stuck with on RHEL 7. zsh is otherwise very simple and everything just works and you probably don't need this snippet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment