From d44150534810b6c309be75363cf4d83d3f023e22 Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Tue, 9 May 2017 16:23:22 -0700 Subject: [PATCH] Add kubens tool for namespace switching Fixes #1. Now kubectx also ships with kubens. Extracted utility functions to a utils.bash file, loaded from ../include/utils.bash. Signed-off-by: Ahmet Alp Balkan --- Formula/kubectx.rb | 7 ++ README.md | 38 ++++++++++- completion/kubens.bash | 8 +++ completion/kubens.zsh | 2 + kubectx | 19 +++--- kubens | 144 +++++++++++++++++++++++++++++++++++++++++ utils.bash | 11 ++++ 7 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 completion/kubens.bash create mode 100644 completion/kubens.zsh create mode 100755 kubens create mode 100644 utils.bash diff --git a/Formula/kubectx.rb b/Formula/kubectx.rb index fde00e9..162dd51 100644 --- a/Formula/kubectx.rb +++ b/Formula/kubectx.rb @@ -3,16 +3,23 @@ class Kubectx < Formula homepage "https://github.com/ahmetb/kubectx" url "https://github.com/ahmetb/kubectx/archive/v0.2.0.tar.gz" sha256 "28069aff84aaba1aa38f42d3b27e64e460a5c0651fb56b1748f44fd832d912e3" + head "https://github.com/ahmetb/kubectx.git", :branch => "master" + bottle :unneeded def install bin.install "kubectx" + bin.install "kubens" + include.install "utils.bash" bash_completion.install "completion/kubectx.bash" => "kubectx" + bash_completion.install "completion/kubens.bash" => "kubens" zsh_completion.install "completion/kubectx.zsh" => "_kubectx" + zsh_completion.install "completion/kubens.zsh" => "_kubens" end test do system "which", "kubectx" + system "which", "kubens" end end diff --git a/README.md b/README.md index 880b3e9..b3a51cf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +This repository provides both `kubectx` and `kubens` tools. Purpose of this +project is to provide an utility and facilitate discussion about how `kubectl` +can manage contexts better. + # kubectx(1) kubectx is an utility to manage and switch between kubectl(1) contexts. @@ -14,7 +18,7 @@ USAGE: Purpose of this project is to provide an utility and facilitate discussion about how `kubectl` can manage contexts better. -## Usage +### Usage ```sh $ kubectx minikube @@ -34,6 +38,38 @@ Aliased "gke_ahmetb_europe-west1-b_dublin" as "dublin". `kubectx` also supports Tab completion, which helps with long context names. +----- + +# kubens(1) + +kubens is an utility to switch between Kubernetes namespaces. + +``` +USAGE: + kubens : list the namespaces + kubens : change the active namespace + kubens - : switch to the previous namespace + kubens -h,--help : show this message +``` + + +### Usage + +```sh +$ kubens kube-system +Context "test" set. +Active namespace is "kube-system". + +$ kubens - +Context "test" set. +Active namespace is "default". +``` + +`kubectx` also supports Tab completion, which helps with long context +names. + +----- + ## Installation For macOS: diff --git a/completion/kubens.bash b/completion/kubens.bash new file mode 100644 index 0000000..eaad17d --- /dev/null +++ b/completion/kubens.bash @@ -0,0 +1,8 @@ +_kube_namespaces() +{ + local curr_arg; + curr_arg=${COMP_WORDS[COMP_CWORD]} + COMPREPLY=( $(compgen -W "- $(kubectl get namespaces -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}')" -- $curr_arg ) ); +} + +complete -F _kube_namespaces kubens diff --git a/completion/kubens.zsh b/completion/kubens.zsh new file mode 100644 index 0000000..f7cf71f --- /dev/null +++ b/completion/kubens.zsh @@ -0,0 +1,2 @@ +#compdef kubens +_arguments "1: :(- $(kubectl get namespaces -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}'))" diff --git a/kubectx b/kubectx index 8cd4342..9434742 100755 --- a/kubectx +++ b/kubectx @@ -21,6 +21,13 @@ set -eou pipefail IFS=$'\n\t' +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +if [[ -f "${SCRIPT_DIR}/utils.bash" ]]; then + source "${SCRIPT_DIR}/utils.bash" +else + source "${SCRIPT_DIR}/../include/utils.bash" +fi + KUBECTX="${HOME}/.kube/kubectx" usage() { @@ -35,14 +42,6 @@ EOF exit 1 } -current_context() { - kubectl config view -o=jsonpath='{.current-context}' -} - -get_contexts() { - kubectl config get-contexts --output=name -} - list_contexts() { set -u pipefail local cur="$(current_context)" @@ -88,7 +87,6 @@ set_context() { } swap_context() { - set -e local ctx="$(read_context)" if [[ -z "${ctx}" ]]; then echo "error: No previous context found." >&2 @@ -138,7 +136,8 @@ main() { elif [[ "${1}" == '-h' || "${1}" == '--help' ]]; then usage elif [[ "${1}" =~ ^-(.*) ]]; then - echo "error: unrecognized flag" >&2; usage + echo "error: unrecognized flag \"${1}\"" >&2 + usage elif [[ "${1}" =~ (.+)=(.+) ]]; then alias_context "${BASH_REMATCH[2]}" "${BASH_REMATCH[1]}" else diff --git a/kubens b/kubens new file mode 100755 index 0000000..9fa5500 --- /dev/null +++ b/kubens @@ -0,0 +1,144 @@ +#!/bin/bash +# +# kubenx(1) is a utility to switch between Kubernetes namespaces. + +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[[ -n $DEBUG ]] && set -x + +set -eou pipefail +IFS=$'\n\t' + +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +if [[ -f "${SCRIPT_DIR}/utils.bash" ]]; then + source "${SCRIPT_DIR}/utils.bash" +else + source "${SCRIPT_DIR}/../include/utils.bash" +fi + +KUBENS_DIR="${HOME}/.kube/kubens" + +usage() { + cat <<"EOF" +USAGE: + kubens : list the namespaces in the current context + kubens : change the active namespace of current context + kubens - : switch to the previous namespace in this context + kubens -h,--help : show this message +EOF + exit 1 +} + +current_namespace() { + local cur_ctx=$(current_context) + ns="$(kubectl config view -o=jsonpath="{.contexts[?(@.name==\"${cur_ctx}\")].context.namespace}")" + if [[ -z "${ns}" ]]; then + echo "default" + else + echo "${ns}" + fi +} + +namespace_file() { + local ctx="${1}" + echo "${KUBENS_DIR}/${ctx}" +} + +read_namespace() { + local f="$(namespace_file "${1}")" + [[ -f "${f}" ]] && cat "${f}" +} + +save_namespace() { + mkdir -p "${KUBENS_DIR}" + local f="$(namespace_file "${1}")" + local saved="$(read_namespace "${1}")" + + if [[ "${saved}" != "${2}" ]]; then + printf %s "${2}" > "${f}" + fi +} + +switch_namespace() { + local ctx="${1}" + kubectl config set-context "${ctx}" --namespace="${2}" + echo "Active namespace is \"${2}\".">&2 +} + +set_namespace() { + local ctx="$(current_context)" + local prev="$(current_namespace)" + + if grep -q ^"${1}"\$ <(get_namespaces); then + switch_namespace "${ctx}" "${1}" + + if [[ "${prev}" != "${1}" ]]; then + save_namespace "${ctx}" "${prev}" + fi + else + echo "error: no namespace exists with name \"${1}\".">&2 + exit 1 + fi +} + +list_namespaces() { + local cur="$(current_namespace)" + + local yellow=$(tput setaf 3) + local darkbg=$(tput setab 0) + local normal=$(tput sgr0) + + for c in $(get_namespaces); do + if [[ "${c}" = "${cur}" ]]; then + echo "${darkbg}${yellow}${c}${normal}" + else + echo "${c}" + fi + done +} + +swap_namespace() { + local ctx="$(current_context)" + local ns="$(read_namespace "${ctx}")" + if [[ -z "${ns}" ]]; then + echo "error: No previous namespace found for current context." >&2 + exit 1 + fi + set_namespace "${ns}" +} + +main() { + if [[ "$#" -eq 0 ]]; then + list_namespaces + elif [[ "$#" -eq 1 ]]; then + if [[ "${1}" == '-h' || "${1}" == '--help' ]]; then + usage + elif [[ "${1}" == "-" ]]; then + swap_namespace + elif [[ "${1}" =~ ^-(.*) ]]; then + echo "error: unrecognized flag \"${1}\"" >&2 + usage + elif [[ "${1}" =~ (.+)=(.+) ]]; then + alias_context "${BASH_REMATCH[2]}" "${BASH_REMATCH[1]}" + else + set_namespace "${1}" + fi + else + echo "error: too many flags" >&2 + usage + fi +} + +main "$@" diff --git a/utils.bash b/utils.bash new file mode 100644 index 0000000..8cfc50c --- /dev/null +++ b/utils.bash @@ -0,0 +1,11 @@ +current_context() { + kubectl config view -o=jsonpath='{.current-context}' +} + +get_contexts() { + kubectl config get-contexts -o=name | sort -n +} + +get_namespaces() { + kubectl get namespaces -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}' +}