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}'
+}