mirror of
https://github.com/ahmetb/kubectx.git
synced 2026-02-22 16:13:30 +00:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcb43fdf1b | ||
|
|
e2f7dc0de2 | ||
|
|
9645e5c62c | ||
|
|
00a1e12bfb | ||
|
|
c3dd1e5deb | ||
|
|
a21638226f | ||
|
|
28e7c12f51 | ||
|
|
1652420a15 | ||
|
|
543e035090 | ||
|
|
a5e810b837 | ||
|
|
62f3f27889 | ||
|
|
4258f03446 | ||
|
|
b9614bd2e0 | ||
|
|
b3732b309e | ||
|
|
a1bce92cc8 | ||
|
|
1356c37cc0 | ||
|
|
10c9bd58ca | ||
|
|
b6e918b084 | ||
|
|
402cc2c4b9 | ||
|
|
df557e4fa7 | ||
|
|
b584d14f90 | ||
|
|
acbf324464 | ||
|
|
845f3b690b | ||
|
|
2b5bf4e429 | ||
|
|
4a7d7cf025 | ||
|
|
dfeb7df363 | ||
|
|
407a84ce9e | ||
|
|
ec994aff89 | ||
|
|
3aeb4e76d2 | ||
|
|
517dae9fc8 | ||
|
|
083e56f221 | ||
|
|
6c94248e98 | ||
|
|
244dd5b8a5 | ||
|
|
121f15d1d3 | ||
|
|
21a1e1e963 | ||
|
|
6811a5f03c | ||
|
|
41296a5fcf | ||
|
|
34a9e100c8 | ||
|
|
365fa23d87 | ||
|
|
ccc077b6c5 | ||
|
|
d931779c0c | ||
|
|
f01719a5a6 | ||
|
|
46d593305a | ||
|
|
595c27ada7 | ||
|
|
8df92316d6 | ||
|
|
7b23263fc2 | ||
|
|
e368d13eea | ||
|
|
7bc9a1277c | ||
|
|
bc46739ab5 | ||
|
|
5a1366b7c9 | ||
|
|
5144c0f958 | ||
|
|
11336883cb | ||
|
|
b1324570ad | ||
|
|
5f4160766d | ||
|
|
c606382a62 | ||
|
|
428fb0045c | ||
|
|
a97ab8367d | ||
|
|
9beb1a1587 | ||
|
|
c23c2a9e29 | ||
|
|
dad48e5397 | ||
|
|
e9fbafc923 | ||
|
|
b7af607a91 | ||
|
|
bcb89389c6 | ||
|
|
4a1d73d5fe | ||
|
|
f986c148b2 | ||
|
|
2799a9e331 | ||
|
|
d61654cb39 | ||
|
|
d1b69a995a | ||
|
|
4520d3f54b | ||
|
|
d906013970 | ||
|
|
c4867bc1f2 | ||
|
|
7b6528a4ae | ||
|
|
07583efe3d | ||
|
|
cce04a3279 | ||
|
|
d6f706a28e | ||
|
|
0141d66224 | ||
|
|
fbce3de6b9 | ||
|
|
6610d70ca8 | ||
|
|
80336137bd | ||
|
|
1c9b5c54e8 | ||
|
|
b188f4da88 | ||
|
|
57893cb668 | ||
|
|
8da629d98e | ||
|
|
b2992aa0df | ||
|
|
2f33693466 | ||
|
|
c813642dc3 | ||
|
|
12575f8ce4 | ||
|
|
46d4236f1a | ||
|
|
7ae68ada83 | ||
|
|
146b67d2b0 | ||
|
|
e3ceb14db5 | ||
|
|
6e250aecb6 | ||
|
|
d613c37c16 | ||
|
|
61f1aa8fd7 | ||
|
|
8390860474 | ||
|
|
394288edc0 | ||
|
|
dc6d2e57b3 | ||
|
|
aa215b9eeb |
11
.travis.yml
Normal file
11
.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:duggan/bats --yes
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq bats
|
||||
- sudo curl -fsSL -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubectl
|
||||
- sudo chmod +x /usr/bin/kubectl
|
||||
script:
|
||||
- bats test/kubectx.bats
|
||||
- bats test/kubens.bats
|
||||
- shellcheck kubectx
|
||||
- shellcheck kubens
|
||||
@@ -1,26 +0,0 @@
|
||||
class Kubectx < Formula
|
||||
desc "Tool that can switch between kubectl contexts easily and create aliases"
|
||||
homepage "https://github.com/ahmetb/kubectx"
|
||||
url "https://github.com/ahmetb/kubectx/archive/v0.3.1.tar.gz"
|
||||
sha256 "4e995f5bec6f41c8d5b6e77f413a58ead077816348e72de26dde3655ec2b7d0b"
|
||||
head "https://github.com/ahmetb/kubectx.git", :branch => "short-names"
|
||||
bottle :unneeded
|
||||
|
||||
option "with-short-names", "link as \"kctx\" and \"kns\" instead"
|
||||
|
||||
def install
|
||||
bin.install "kubectx" => build.with?("short-names") ? "kctx" : "kubectx"
|
||||
bin.install "kubens" => build.with?("short-names") ? "kns" : "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", build.with?("short-names") ? "kctx" : "kubectx"
|
||||
system "which", build.with?("short-names") ? "kns" : "kubens"
|
||||
end
|
||||
end
|
||||
171
README.md
171
README.md
@@ -1,23 +1,35 @@
|
||||
# `kubectx` + `kubens`: Power tools for kubectl
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
This repository provides both `kubectx` and `kubens` tools.
|
||||
|
||||
|
||||
**`kubectx`** help you switch between clusters back and forth:
|
||||
**`kubectx`** helps you switch between clusters back and forth:
|
||||

|
||||
|
||||
**`kubens`** help you switch between Kubernetes namespaces smoothly:
|
||||
**`kubens`** helps you switch between Kubernetes namespaces smoothly:
|
||||

|
||||
|
||||
# kubectx(1)
|
||||
|
||||
kubectx is an utility to manage and switch between kubectl(1) contexts.
|
||||
kubectx is a utility to manage and switch between kubectl(1) contexts.
|
||||
|
||||
```
|
||||
USAGE:
|
||||
kubectx : list the contexts
|
||||
kubectx <NAME> : switch to context <NAME>
|
||||
kubectx - : switch to the previous context
|
||||
kubectx -c, --current : show the current context name
|
||||
kubectx <NEW_NAME>=<NAME> : rename context <NAME> to <NEW_NAME>
|
||||
kubectx -h,--help : show this message
|
||||
kubectx <NEW_NAME>=. : rename current-context to <NEW_NAME>
|
||||
kubectx -d <NAME> : delete context <NAME> ('.' for current-context)
|
||||
(this command won't delete the user/cluster entry
|
||||
that is used by the context)
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -37,21 +49,21 @@ Context "dublin" set.
|
||||
Aliased "gke_ahmetb_europe-west1-b_dublin" as "dublin".
|
||||
```
|
||||
|
||||
`kubectx` supports <kbd>Tab</kbd> completion on bash/zsh shells to help with
|
||||
`kubectx` supports <kbd>Tab</kbd> completion on bash/zsh/fish shells to help with
|
||||
long context names. You don't have to remember full context names anymore.
|
||||
|
||||
-----
|
||||
|
||||
# kubens(1)
|
||||
|
||||
kubens is an utility to switch between Kubernetes namespaces.
|
||||
kubens is a utility to switch between Kubernetes namespaces.
|
||||
|
||||
```
|
||||
USAGE:
|
||||
kubens : list the namespaces
|
||||
kubens <NAME> : change the active namespace
|
||||
kubens - : switch to the previous namespace
|
||||
kubens -h,--help : show this message
|
||||
kubens -c, --current : show the current namespace
|
||||
```
|
||||
|
||||
|
||||
@@ -67,35 +79,142 @@ Context "test" set.
|
||||
Active namespace is "default".
|
||||
```
|
||||
|
||||
`kubens` also supports <kbd>Tab</kbd> completion on bash/zsh shells.
|
||||
`kubens` also supports <kbd>Tab</kbd> completion on bash/zsh/fish shells.
|
||||
|
||||
-----
|
||||
|
||||
## Installation
|
||||
|
||||
**For macOS:**
|
||||
### macOS
|
||||
|
||||
:tada: kubectx is now in Homebrew! :confetti_ball:
|
||||
#### Homebrew
|
||||
|
||||
> Use the [Homebrew](https://brew.sh/) package manager:
|
||||
>
|
||||
> brew install kubectx
|
||||
>
|
||||
> this will also set up bash/zsh completion scripts automatically.
|
||||
:confetti_ball: If you use [Homebrew](https://brew.sh/) you can install like this:
|
||||
|
||||
Running `brew install` with `--with-short-names` will install tools with names
|
||||
`kctx` and `kns` to prevent prefix collision with `kubectl` name.
|
||||
brew install kubectx
|
||||
|
||||
> Note: If you installed kubectx before it was accepted to Homebrew core
|
||||
> repository, reinstall with:
|
||||
> `brew untap ahmetb/kubectx && brew uninstall --force kubectx && brew update && brew install kubectx`
|
||||
This command will set up bash/zsh/fish completion scripts automatically.
|
||||
|
||||
**Other platforms:**
|
||||
- If you like to add context/namespace info to your shell prompt (`$PS1`),
|
||||
I recommend trying out [kube-ps1](https://github.com/jonmosco/kube-ps1).
|
||||
|
||||
- Download the `kubectx` script
|
||||
- Add it somewhere in your PATH
|
||||
- Make it executable (`chmod +x`)
|
||||
- You can also install bash/zsh [completion scripts](completion/) manually.
|
||||
#### MacPorts
|
||||
|
||||
If you use [MacPorts](https://www.macports.org) you can install like this:
|
||||
|
||||
sudo port install kubectx
|
||||
|
||||
### Linux
|
||||
|
||||
Since `kubectx`/`kubens` are written in Bash, you should be able to install
|
||||
them to any POSIX environment that has Bash installed.
|
||||
|
||||
- Download the `kubectx`, and `kubens` scripts.
|
||||
- Either:
|
||||
- save them all to somewhere in your `PATH`,
|
||||
- or save them to a directory, then create symlinks to `kubectx`/`kubens` from
|
||||
somewhere in your `PATH`, like `/usr/local/bin`
|
||||
- Make `kubectx` and `kubens` executable (`chmod +x ...`)
|
||||
- Install bash/zsh/fish [completion scripts](completion/).
|
||||
- For zsh:
|
||||
The completion scripts have to be in a path that belongs to `$fpath`. Either link or copy them to an existing folder.
|
||||
If using oh-my-zsh you can do as follows:
|
||||
```bash
|
||||
mkdir -p ~/.oh-my-zsh/completions
|
||||
chmod -R 755 ~/.oh-my-zsh/completions
|
||||
ln -s /opt/kubectx/completion/kubectx.zsh ~/.oh-my-zsh/completions/_kubectx.zsh
|
||||
ln -s /opt/kubectx/completion/kubens.zsh ~/.oh-my-zsh/completions/_kubens.zsh
|
||||
```
|
||||
Note that the leading underscore seems to be a convention. If completion doesn't work, add `autoload -U compinit && compinit` to your `.zshrc` (similar to [`zsh-completions`](https://github.com/zsh-users/zsh-completions/blob/master/README.md#oh-my-zsh)).
|
||||
If not using oh-my-zsh, you could link to `/usr/share/zsh/functions/Completion` (might require sudo), depending on the `$fpath` of your zsh installation.
|
||||
In case of error, calling `compaudit` might help.
|
||||
- For bash:
|
||||
```bash
|
||||
git clone https://github.com/ahmetb/kubectx.git ~/.kubectx
|
||||
COMPDIR=$(pkg-config --variable=completionsdir bash-completion)
|
||||
ln -sf ~/.kubectx/completion/kubens.bash $COMPDIR/kubens
|
||||
ln -sf ~/.kubectx/completion/kubectx.bash $COMPDIR/kubectx
|
||||
cat << FOE >> ~/.bashrc
|
||||
|
||||
|
||||
#kubectx and kubens
|
||||
export PATH=~/.kubectx:\$PATH
|
||||
FOE
|
||||
```
|
||||
- For fish:
|
||||
```fish
|
||||
mkdir -p ~/.config/fish/completions
|
||||
ln -s /opt/kubectx/completion/kubectx.fish ~/.config/fish/completions/
|
||||
ln -s /opt/kubectx/completion/kubens.fish ~/.config/fish/completions/
|
||||
```
|
||||
|
||||
Example installation steps:
|
||||
|
||||
``` bash
|
||||
sudo git clone https://github.com/ahmetb/kubectx /opt/kubectx
|
||||
sudo ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx
|
||||
sudo ln -s /opt/kubectx/kubens /usr/local/bin/kubens
|
||||
```
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
Available as official Arch Linux package. Install it via:
|
||||
|
||||
```bash
|
||||
sudo pacman -S kubectx
|
||||
```
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
Available as a Debian package for [Debian Buster (testing)](https://packages.debian.org/buster/kubectx), [Sid (unstable)](https://packages.debian.org/sid/kubectx) (_note: if you are unfamiliar with Debian release process and how to enable testing/unstable repos, check the [Debian Wiki](https://wiki.debian.org/DebianReleases)_):
|
||||
|
||||
``` bash
|
||||
sudo apt install kubectx
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
### Interactive mode
|
||||
|
||||
If you want `kubectx` and `kubens` commands to present you an interactive menu
|
||||
with fuzzy searching, you just need to [install
|
||||
`fzf`](https://github.com/junegunn/fzf) in your PATH.
|
||||
|
||||

|
||||
|
||||
If you have `fzf` installed, but want to opt out of using this feature, set the environment variable `KUBECTX_IGNORE_FZF=1`.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
### Customizing colors
|
||||
|
||||
If you like to customize the colors indicating the current namespace or context, set the environment variables `KUBECTX_CURRENT_FGCOLOR` and `KUBECTX_CURRENT_BGCOLOR` (refer color codes [here](https://linux.101hacks.com/ps1-examples/prompt-color-using-tput/)):
|
||||
|
||||
```
|
||||
export KUBECTX_CURRENT_FGCOLOR=$(tput setaf 6) # blue text
|
||||
export KUBECTX_CURRENT_BGCOLOR=$(tput setab 7) # white background
|
||||
```
|
||||
|
||||
Colors in the output can be disabled by setting the
|
||||
[`NO_COLOR`](http://no-color.org/) environment variable.
|
||||
|
||||
-----
|
||||
|
||||
#### Users
|
||||
|
||||
| What are others saying about kubectx? |
|
||||
| ---- |
|
||||
| _“Thank you for kubectx & kubens - I use them all the time & have them in my k8s toolset to maintain happiness :) ”_ – [@pbouwer](https://twitter.com/pbouwer/status/925896377929949184) |
|
||||
| _“I can't imagine working without kubectx and especially kubens anymore. It's pure gold.”_ – [@timoreimann](https://twitter.com/timoreimann/status/925801946757419008) |
|
||||
| _“I'm liking kubectx from @ahmetb, makes it super-easy to switch #Kubernetes contexts [...]”_ — [@lizrice](https://twitter.com/lizrice/status/928556415517589505) |
|
||||
| _“Also using it on a daily basis. This and my zsh config that shows me the current k8s context 😉”_ – [@puja108](https://twitter.com/puja108/status/928742521139810305) |
|
||||
| _“Lately I've found myself using the kubens command more than kubectx. Both very useful though :-)”_ – [@stuartleeks](https://twitter.com/stuartleeks/status/928562850464907264) |
|
||||
| _“yeah kubens rocks!”_ – [@embano1](https://twitter.com/embano1/status/928698440732815360) |
|
||||
| _“Special thanks to Ahmet Alp Balkan for creating kubectx, kubens, and kubectl aliases, as these tools made my life better.”_ – [@strebeld](https://medium.com/@strebeld/5-ways-to-enhance-kubectl-ux-97c8893227a) |
|
||||
| _“❤️ this shell script @ahmetb wrote to help make switching between kubectl config contexts a breeze.”_ – [@briandanowski](https://twitter.com/briandanowski/status/1085409568165896193) |
|
||||
|
||||
> If you liked `kubectx`, you may like my [`kubectl-aliases`](https://github.com/ahmetb/kubectl-aliases) project, too.
|
||||
|
||||
-----
|
||||
|
||||
@@ -105,4 +224,4 @@ Disclaimer: This is not an official Google product.
|
||||
#### Stargazers over time
|
||||
|
||||
[](https://starcharts.herokuapp.com/ahmetb/kubectx)
|
||||
|
||||

|
||||
|
||||
3
completion/kubectx.fish
Normal file
3
completion/kubectx.fish
Normal file
@@ -0,0 +1,3 @@
|
||||
# kubectx
|
||||
complete -f -c kubectx -a "- (kubectl config get-contexts --output='name')"
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
|
||||
local KUBECTX="${HOME}/.kube/kubectx"
|
||||
PREV=""
|
||||
|
||||
local all_contexts="$(kubectl config get-contexts --output='name')"
|
||||
if [ -f "$KUBECTX" ]; then
|
||||
# show '-' only if there's a saved previous context
|
||||
local PREV=$(cat "${KUBECTX}")
|
||||
_arguments "1: :((- \
|
||||
$(kubectl config get-contexts --output='name')))"
|
||||
|
||||
_arguments \
|
||||
"-d:*: :(${all_contexts})" \
|
||||
"(- *): :(- ${all_contexts})"
|
||||
else
|
||||
_arguments "1: :($(kubectl config get-contexts --output='name'))"
|
||||
_arguments \
|
||||
"-d:*: :(${all_contexts})" \
|
||||
"(- *): :(${all_contexts})"
|
||||
fi
|
||||
|
||||
3
completion/kubens.fish
Normal file
3
completion/kubens.fish
Normal file
@@ -0,0 +1,3 @@
|
||||
# kubens
|
||||
complete -f -c kubens -a "(kubectl get ns -o=custom-columns=NAME:.metadata.name --no-headers)"
|
||||
|
||||
BIN
img/kubectx-interactive.gif
Normal file
BIN
img/kubectx-interactive.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
173
kubectx
173
kubectx
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# kubectx(1) is a utility to manage and switch between kubectl contexts.
|
||||
|
||||
@@ -21,39 +21,68 @@
|
||||
set -eou pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
SCRIPT_DIR="$(dirname "$( readlink -f "${0}" 2>/dev/null || \
|
||||
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "${0}" )")"
|
||||
|
||||
if [[ -f "${SCRIPT_DIR}/utils.bash" ]]; then
|
||||
source "${SCRIPT_DIR}/utils.bash"
|
||||
else
|
||||
source "${SCRIPT_DIR}/../include/utils.bash"
|
||||
SELF_CMD="$0"
|
||||
SELF="kubectx"
|
||||
if [[ "$(basename "$0")" == kubectl-* ]]; then # invoked as plugin
|
||||
SELF="kubectl ctx"
|
||||
fi
|
||||
|
||||
KUBECTX="${HOME}/.kube/kubectx"
|
||||
KUBECTX="${XDG_CACHE_HOME:-$HOME/.kube}/kubectx"
|
||||
|
||||
usage() {
|
||||
cat <<"EOF"
|
||||
cat <<EOF
|
||||
USAGE:
|
||||
kubectx : list the contexts
|
||||
kubectx <NAME> : switch to context <NAME>
|
||||
kubectx - : switch to the previous context
|
||||
kubectx <NEW_NAME>=<NAME> : rename context <NAME> to <NEW_NAME>
|
||||
kubectx -h,--help : show this message
|
||||
$SELF : list the contexts
|
||||
$SELF <NAME> : switch to context <NAME>
|
||||
$SELF - : switch to the previous context
|
||||
$SELF -c, --current : show the current context name
|
||||
$SELF <NEW_NAME>=<NAME> : rename context <NAME> to <NEW_NAME>
|
||||
$SELF <NEW_NAME>=. : rename current-context to <NEW_NAME>
|
||||
$SELF -d <NAME> [<NAME...>] : delete context <NAME> ('.' for current-context)
|
||||
(this command won't delete the user/cluster entry
|
||||
that is used by the context)
|
||||
|
||||
$SELF -h,--help : show this message
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit_err() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
current_context() {
|
||||
$KUBECTL config view -o=jsonpath='{.current-context}'
|
||||
}
|
||||
|
||||
get_contexts() {
|
||||
$KUBECTL config get-contexts -o=name | sort -n
|
||||
}
|
||||
|
||||
list_contexts() {
|
||||
set -u pipefail
|
||||
local cur="$(current_context)"
|
||||
local yellow=$(tput setaf 3)
|
||||
local darkbg=$(tput setab 0)
|
||||
local normal=$(tput sgr0)
|
||||
local cur ctx_list
|
||||
cur="$(current_context)" || exit_err "error getting current context"
|
||||
ctx_list=$(get_contexts) || exit_err "error getting context list"
|
||||
|
||||
for c in $(get_contexts); do
|
||||
if [[ "${c}" = "${cur}" ]]; then
|
||||
echo "${darkbg}${yellow}${c}${normal}"
|
||||
local yellow darkbg normal
|
||||
yellow=$(tput setaf 3 || true)
|
||||
darkbg=$(tput setab 0 || true)
|
||||
normal=$(tput sgr0 || true)
|
||||
|
||||
local cur_ctx_fg cur_ctx_bg
|
||||
cur_ctx_fg=${KUBECTX_CURRENT_FGCOLOR:-$yellow}
|
||||
cur_ctx_bg=${KUBECTX_CURRENT_BGCOLOR:-$darkbg}
|
||||
|
||||
for c in $ctx_list; do
|
||||
if [[ -n "${_KUBECTX_FORCE_COLOR:-}" || \
|
||||
-t 1 && -z "${NO_COLOR:-}" ]]; then
|
||||
# colored output mode
|
||||
if [[ "${c}" = "${cur}" ]]; then
|
||||
echo "${cur_ctx_bg}${cur_ctx_fg}${c}${normal}"
|
||||
else
|
||||
echo "${c}"
|
||||
fi
|
||||
else
|
||||
echo "${c}"
|
||||
fi
|
||||
@@ -67,7 +96,8 @@ read_context() {
|
||||
}
|
||||
|
||||
save_context() {
|
||||
local saved="$(read_context)"
|
||||
local saved
|
||||
saved="$(read_context)"
|
||||
|
||||
if [[ "${saved}" != "${1}" ]]; then
|
||||
printf %s "${1}" > "${KUBECTX}"
|
||||
@@ -75,11 +105,25 @@ save_context() {
|
||||
}
|
||||
|
||||
switch_context() {
|
||||
kubectl config use-context "${1}"
|
||||
$KUBECTL config use-context "${1}"
|
||||
}
|
||||
|
||||
choose_context_interactive() {
|
||||
local choice
|
||||
choice="$(_KUBECTX_FORCE_COLOR=1 \
|
||||
FZF_DEFAULT_COMMAND="${SELF_CMD}" \
|
||||
fzf --ansi --no-preview || true)"
|
||||
if [[ -z "${choice}" ]]; then
|
||||
echo 2>&1 "error: you did not choose any of the options"
|
||||
exit 1
|
||||
else
|
||||
set_context "${choice}"
|
||||
fi
|
||||
}
|
||||
|
||||
set_context() {
|
||||
local prev="$(current_context)"
|
||||
local prev
|
||||
prev="$(current_context)" || exit_err "error getting current context"
|
||||
|
||||
switch_context "${1}"
|
||||
|
||||
@@ -89,7 +133,8 @@ set_context() {
|
||||
}
|
||||
|
||||
swap_context() {
|
||||
local ctx="$(read_context)"
|
||||
local ctx
|
||||
ctx="$(read_context)"
|
||||
if [[ -z "${ctx}" ]]; then
|
||||
echo "error: No previous context found." >&2
|
||||
exit 1
|
||||
@@ -97,45 +142,88 @@ swap_context() {
|
||||
set_context "${ctx}"
|
||||
}
|
||||
|
||||
user_of_context() {
|
||||
kubectl config view \
|
||||
-o=jsonpath="{.contexts[?(@.name==\"${1}\")].context.user}"
|
||||
}
|
||||
|
||||
cluster_of_context() {
|
||||
kubectl config view \
|
||||
-o=jsonpath="{.contexts[?(@.name==\"${1}\")].context.cluster}"
|
||||
context_exists() {
|
||||
grep -q ^"${1}"\$ <($KUBECTL config get-contexts -o=name)
|
||||
}
|
||||
|
||||
rename_context() {
|
||||
local old_name="${1}"
|
||||
local new_name="${2}"
|
||||
|
||||
local old_user="$(user_of_context "${old_name}")"
|
||||
local old_cluster="$(cluster_of_context "${old_name}")"
|
||||
if [[ "${old_name}" == "." ]]; then
|
||||
old_name="$(current_context)"
|
||||
fi
|
||||
|
||||
if [[ -z "$old_user" || -z "$old_cluster" ]]; then
|
||||
echo "error: Cannot retrieve context ${old_name}." >&2
|
||||
if ! context_exists "${old_name}"; then
|
||||
echo "error: Context \"${old_name}\" not found, can't rename it." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kubectl config rename-context "${old_name}" "${new_name}"
|
||||
if context_exists "${new_name}"; then
|
||||
echo "Context \"${new_name}\" exists, deleting..." >&2
|
||||
$KUBECTL config delete-context "${new_name}" 1>/dev/null 2>&1
|
||||
fi
|
||||
|
||||
$KUBECTL config rename-context "${old_name}" "${new_name}"
|
||||
}
|
||||
|
||||
delete_contexts() {
|
||||
for i in "${@}"; do
|
||||
delete_context "${i}"
|
||||
done
|
||||
}
|
||||
|
||||
delete_context() {
|
||||
local ctx
|
||||
ctx="${1}"
|
||||
if [[ "${ctx}" == "." ]]; then
|
||||
ctx="$(current_context)" || exit_err "error getting current context"
|
||||
fi
|
||||
echo "Deleting context \"${ctx}\"..." >&2
|
||||
$KUBECTL config delete-context "${ctx}"
|
||||
}
|
||||
|
||||
main() {
|
||||
if hash kubectl 2>/dev/null; then
|
||||
KUBECTL=kubectl
|
||||
elif hash kubectl.exe 2>/dev/null; then
|
||||
KUBECTL=kubectl.exe
|
||||
else
|
||||
echo >&2 "kubectl is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
list_contexts
|
||||
if [[ -t 1 && -z "${KUBECTX_IGNORE_FZF:-}" && "$(type fzf &>/dev/null; echo $?)" -eq 0 ]]; then
|
||||
choose_context_interactive
|
||||
else
|
||||
list_contexts
|
||||
fi
|
||||
elif [[ "${1}" == "-d" ]]; then
|
||||
if [[ "$#" -lt 2 ]]; then
|
||||
echo "error: missing context NAME" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
delete_contexts "${@:2}"
|
||||
elif [[ "$#" -gt 1 ]]; then
|
||||
echo "error: too many flags" >&2
|
||||
echo "error: too many arguments" >&2
|
||||
usage
|
||||
exit 1
|
||||
elif [[ "$#" -eq 1 ]]; then
|
||||
if [[ "${1}" == "-" ]]; then
|
||||
swap_context
|
||||
elif [[ "${1}" == '-c' || "${1}" == '--current' ]]; then
|
||||
# we don't call current_context here for two reasons:
|
||||
# - it does not fail when current-context property is not set
|
||||
# - it does not return a trailing newline
|
||||
kubectl config current-context
|
||||
elif [[ "${1}" == '-h' || "${1}" == '--help' ]]; then
|
||||
usage
|
||||
elif [[ "${1}" =~ ^-(.*) ]]; then
|
||||
echo "error: unrecognized flag \"${1}\"" >&2
|
||||
usage
|
||||
exit 1
|
||||
elif [[ "${1}" =~ (.+)=(.+) ]]; then
|
||||
rename_context "${BASH_REMATCH[2]}" "${BASH_REMATCH[1]}"
|
||||
else
|
||||
@@ -143,6 +231,7 @@ main() {
|
||||
fi
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
146
kubens
146
kubens
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# kubenx(1) is a utility to switch between Kubernetes namespaces.
|
||||
# kubens(1) is a utility to switch between Kubernetes namespaces.
|
||||
|
||||
# Copyright 2017 Google Inc.
|
||||
#
|
||||
@@ -21,31 +21,37 @@
|
||||
set -eou pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
SCRIPT_DIR="$(dirname "$( readlink -f "${0}" 2>/dev/null || \
|
||||
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "${0}" )")"
|
||||
|
||||
if [[ -f "${SCRIPT_DIR}/utils.bash" ]]; then
|
||||
source "${SCRIPT_DIR}/utils.bash"
|
||||
else
|
||||
source "${SCRIPT_DIR}/../include/utils.bash"
|
||||
SELF_CMD="$0"
|
||||
SELF="kubens"
|
||||
if [[ "$(basename "$0")" == kubectl-* ]]; then # invoked as plugin
|
||||
SELF="kubectl ns"
|
||||
fi
|
||||
|
||||
KUBENS_DIR="${HOME}/.kube/kubens"
|
||||
KUBENS_DIR="${XDG_CACHE_HOME:-$HOME/.kube}/kubens"
|
||||
|
||||
usage() {
|
||||
cat <<"EOF"
|
||||
cat <<EOF
|
||||
USAGE:
|
||||
kubens : list the namespaces in the current context
|
||||
kubens <NAME> : change the active namespace of current context
|
||||
kubens - : switch to the previous namespace in this context
|
||||
kubens -h,--help : show this message
|
||||
$SELF : list the namespaces in the current context
|
||||
$SELF <NAME> : change the active namespace of current context
|
||||
$SELF - : switch to the previous namespace in this context
|
||||
$SELF -c, --current : show the current namespace
|
||||
$SELF -h,--help : show this message
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit_err() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
current_namespace() {
|
||||
local cur_ctx=$(current_context)
|
||||
ns="$(kubectl config view -o=jsonpath="{.contexts[?(@.name==\"${cur_ctx}\")].context.namespace}")"
|
||||
local cur_ctx
|
||||
|
||||
cur_ctx="$(current_context)" || exit_err "error getting current context"
|
||||
ns="$($KUBECTL config view -o=jsonpath="{.contexts[?(@.name==\"${cur_ctx}\")].context.namespace}")" \
|
||||
|| exit_err "error getting current namespace"
|
||||
|
||||
if [[ -z "${ns}" ]]; then
|
||||
echo "default"
|
||||
else
|
||||
@@ -53,20 +59,37 @@ current_namespace() {
|
||||
fi
|
||||
}
|
||||
|
||||
current_context() {
|
||||
$KUBECTL config current-context
|
||||
}
|
||||
|
||||
get_namespaces() {
|
||||
$KUBECTL get namespaces -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}'
|
||||
}
|
||||
|
||||
escape_context_name() {
|
||||
echo "${1//\//-}"
|
||||
}
|
||||
|
||||
namespace_file() {
|
||||
local ctx="${1}"
|
||||
local ctx
|
||||
|
||||
ctx="$(escape_context_name "${1}")"
|
||||
echo "${KUBENS_DIR}/${ctx}"
|
||||
}
|
||||
|
||||
read_namespace() {
|
||||
local f="$(namespace_file "${1}")"
|
||||
local f
|
||||
f="$(namespace_file "${1}")"
|
||||
[[ -f "${f}" ]] && cat "${f}"
|
||||
return 0
|
||||
}
|
||||
|
||||
save_namespace() {
|
||||
mkdir -p "${KUBENS_DIR}"
|
||||
local f="$(namespace_file "${1}")"
|
||||
local saved="$(read_namespace "${1}")"
|
||||
local f saved
|
||||
f="$(namespace_file "${1}")"
|
||||
saved="$(read_namespace "${1}")"
|
||||
|
||||
if [[ "${saved}" != "${2}" ]]; then
|
||||
printf %s "${2}" > "${f}"
|
||||
@@ -75,13 +98,35 @@ save_namespace() {
|
||||
|
||||
switch_namespace() {
|
||||
local ctx="${1}"
|
||||
kubectl config set-context "${ctx}" --namespace="${2}"
|
||||
$KUBECTL config set-context "${ctx}" --namespace="${2}"
|
||||
echo "Active namespace is \"${2}\".">&2
|
||||
}
|
||||
|
||||
choose_namespace_interactive() {
|
||||
# directly calling kubens via fzf might fail with a cryptic error like
|
||||
# "$FZF_DEFAULT_COMMAND failed", so try to see if we can list namespaces
|
||||
# locally first
|
||||
if [[ -z "$(list_namespaces)" ]]; then
|
||||
echo >&2 "error: could not list namespaces (is the cluster accessible?)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local choice
|
||||
choice="$(_KUBECTX_FORCE_COLOR=1 \
|
||||
FZF_DEFAULT_COMMAND="${SELF_CMD}" \
|
||||
fzf --ansi --no-preview || true)"
|
||||
if [[ -z "${choice}" ]]; then
|
||||
echo 2>&1 "error: you did not choose any of the options"
|
||||
exit 1
|
||||
else
|
||||
set_namespace "${choice}"
|
||||
fi
|
||||
}
|
||||
|
||||
set_namespace() {
|
||||
local ctx="$(current_context)"
|
||||
local prev="$(current_namespace)"
|
||||
local ctx prev
|
||||
ctx="$(current_context)" || exit_err "error getting current context"
|
||||
prev="$(current_namespace)" || exit_error "error getting current namespace"
|
||||
|
||||
if grep -q ^"${1}"\$ <(get_namespaces); then
|
||||
switch_namespace "${ctx}" "${1}"
|
||||
@@ -96,24 +141,38 @@ set_namespace() {
|
||||
}
|
||||
|
||||
list_namespaces() {
|
||||
local cur="$(current_namespace)"
|
||||
local yellow darkbg normal
|
||||
yellow=$(tput setaf 3 || true)
|
||||
darkbg=$(tput setab 0 || true)
|
||||
normal=$(tput sgr0 || true)
|
||||
|
||||
local yellow=$(tput setaf 3)
|
||||
local darkbg=$(tput setab 0)
|
||||
local normal=$(tput sgr0)
|
||||
local cur_ctx_fg cur_ctx_bg
|
||||
cur_ctx_fg=${KUBECTX_CURRENT_FGCOLOR:-$yellow}
|
||||
cur_ctx_bg=${KUBECTX_CURRENT_BGCOLOR:-$darkbg}
|
||||
|
||||
for c in $(get_namespaces); do
|
||||
local cur ns_list
|
||||
cur="$(current_namespace)" || exit_err "error getting current namespace"
|
||||
ns_list=$(get_namespaces) || exit_err "error getting namespace list"
|
||||
|
||||
for c in $ns_list; do
|
||||
if [[ -n "${_KUBECTX_FORCE_COLOR:-}" || \
|
||||
-t 1 && -z "${NO_COLOR:-}" ]]; then
|
||||
# colored output mode
|
||||
if [[ "${c}" = "${cur}" ]]; then
|
||||
echo "${darkbg}${yellow}${c}${normal}"
|
||||
echo "${cur_ctx_bg}${cur_ctx_fg}${c}${normal}"
|
||||
else
|
||||
echo "${c}"
|
||||
fi
|
||||
else
|
||||
echo "${c}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
swap_namespace() {
|
||||
local ctx="$(current_context)"
|
||||
local ns="$(read_namespace "${ctx}")"
|
||||
local ctx ns
|
||||
ctx="$(current_context)" || exit_err "error getting current context"
|
||||
ns="$(read_namespace "${ctx}")"
|
||||
if [[ -z "${ns}" ]]; then
|
||||
echo "error: No previous namespace found for current context." >&2
|
||||
exit 1
|
||||
@@ -122,16 +181,34 @@ swap_namespace() {
|
||||
}
|
||||
|
||||
main() {
|
||||
if [[ -z "${KUBECTL:-}" ]]; then
|
||||
if hash kubectl 2>/dev/null; then
|
||||
KUBECTL=kubectl
|
||||
elif hash kubectl.exe 2>/dev/null; then
|
||||
KUBECTL=kubectl.exe
|
||||
else
|
||||
echo >&2 "kubectl is not installed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
list_namespaces
|
||||
if [[ -t 1 && -z ${KUBECTX_IGNORE_FZF:-} && "$(type fzf &>/dev/null; echo $?)" -eq 0 ]]; then
|
||||
choose_namespace_interactive
|
||||
else
|
||||
list_namespaces
|
||||
fi
|
||||
elif [[ "$#" -eq 1 ]]; then
|
||||
if [[ "${1}" == '-h' || "${1}" == '--help' ]]; then
|
||||
usage
|
||||
elif [[ "${1}" == "-" ]]; then
|
||||
swap_namespace
|
||||
elif [[ "${1}" == '-c' || "${1}" == '--current' ]]; then
|
||||
current_namespace
|
||||
elif [[ "${1}" =~ ^-(.*) ]]; then
|
||||
echo "error: unrecognized flag \"${1}\"" >&2
|
||||
usage
|
||||
exit 1
|
||||
elif [[ "${1}" =~ (.+)=(.+) ]]; then
|
||||
alias_context "${BASH_REMATCH[2]}" "${BASH_REMATCH[1]}"
|
||||
else
|
||||
@@ -140,6 +217,7 @@ main() {
|
||||
else
|
||||
echo "error: too many flags" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
30
test/common.bash
Normal file
30
test/common.bash
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# bats setup function
|
||||
setup() {
|
||||
export XDG_CACHE_HOME="$(mktemp -d)"
|
||||
export KUBECONFIG="${XDG_CACHE_HOME}/config"
|
||||
}
|
||||
|
||||
# bats teardown function
|
||||
teardown() {
|
||||
rm -rf "$XDG_CACHE_HOME"
|
||||
}
|
||||
|
||||
use_config() {
|
||||
cp "$BATS_TEST_DIRNAME/testdata/$1" $KUBECONFIG
|
||||
}
|
||||
|
||||
# wrappers around "kubectl config" command
|
||||
|
||||
get_namespace() {
|
||||
kubectl config view -o=jsonpath="{.contexts[?(@.name==\"$(get_context)\")].context.namespace}"
|
||||
}
|
||||
|
||||
get_context() {
|
||||
kubectl config current-context
|
||||
}
|
||||
|
||||
switch_context() {
|
||||
kubectl config use-context "${1}"
|
||||
}
|
||||
241
test/kubectx.bats
Normal file
241
test/kubectx.bats
Normal file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
COMMAND="$BATS_TEST_DIRNAME/../kubectx"
|
||||
|
||||
load common
|
||||
|
||||
@test "no kubectl detected" {
|
||||
OLDPATH="$PATH"
|
||||
PATH=/bin
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" = "kubectl is not installed" ]]
|
||||
PATH="$OLDPATH"
|
||||
}
|
||||
|
||||
@test "--help should not fail" {
|
||||
run ${COMMAND} --help
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "-h should not fail" {
|
||||
run ${COMMAND} -h
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "switch to previous context when no one exists" {
|
||||
use_config config1
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" = "error: No previous context found." ]]
|
||||
}
|
||||
|
||||
@test "list contexts when no kubeconfig exists" {
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" = "" ]]
|
||||
}
|
||||
|
||||
@test "get one context and list contexts" {
|
||||
use_config config1
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" = "user1@cluster1" ]]
|
||||
}
|
||||
|
||||
@test "get two contexts and list contexts" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" = *"user1@cluster1"* ]]
|
||||
[[ "$output" = *"user2@cluster1"* ]]
|
||||
}
|
||||
|
||||
@test "get two contexts and select contexts" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} user1@cluster1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$(get_context)"
|
||||
[[ "$(get_context)" = "user1@cluster1" ]]
|
||||
|
||||
run ${COMMAND} user2@cluster1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$(get_context)"
|
||||
[[ "$(get_context)" = "user2@cluster1" ]]
|
||||
}
|
||||
|
||||
@test "get two contexts and switch between contexts" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} user1@cluster1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$(get_context)"
|
||||
[[ "$(get_context)" = "user1@cluster1" ]]
|
||||
|
||||
run ${COMMAND} user2@cluster1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$(get_context)"
|
||||
[[ "$(get_context)" = "user2@cluster1" ]]
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$(get_context)"
|
||||
[[ "$(get_context)" = "user1@cluster1" ]]
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$(get_context)"
|
||||
[[ "$(get_context)" = "user2@cluster1" ]]
|
||||
}
|
||||
|
||||
@test "get one context and switch to non existent context" {
|
||||
use_config config1
|
||||
|
||||
run ${COMMAND} "unknown-context"
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "-c/--current fails when no context set" {
|
||||
use_config config1
|
||||
|
||||
run "${COMMAND}" -c
|
||||
echo "$output"
|
||||
[ $status -eq 1 ]
|
||||
run "${COMMAND}" --current
|
||||
echo "$output"
|
||||
[ $status -eq 1 ]
|
||||
}
|
||||
|
||||
@test "-c/--current prints the current context" {
|
||||
use_config config1
|
||||
|
||||
run "${COMMAND}" user1@cluster1
|
||||
[ $status -eq 0 ]
|
||||
|
||||
run "${COMMAND}" -c
|
||||
echo "$output"
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" = "user1@cluster1" ]]
|
||||
run "${COMMAND}" --current
|
||||
echo "$output"
|
||||
[ $status -eq 0 ]
|
||||
[[ "$output" = "user1@cluster1" ]]
|
||||
}
|
||||
|
||||
@test "rename context" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} "new-context=user1@cluster1"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" = *"user1@cluster1"* ]]
|
||||
[[ "$output" = *"new-context"* ]]
|
||||
[[ "$output" = *"user2@cluster1"* ]]
|
||||
}
|
||||
|
||||
@test "rename current context" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} user2@cluster1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND} new-context=.
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" = *"user2@cluster1"* ]]
|
||||
[[ "$output" = *"user1@cluster1"* ]]
|
||||
[[ "$output" = *"new-context"* ]]
|
||||
}
|
||||
|
||||
@test "delete context" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} -d "user1@cluster1"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" = "user1@cluster1" ]]
|
||||
[[ "$output" = "user2@cluster1" ]]
|
||||
}
|
||||
|
||||
@test "delete current context" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} user2@cluster1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND} -d .
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" = "user2@cluster1" ]]
|
||||
[[ "$output" = "user1@cluster1" ]]
|
||||
}
|
||||
|
||||
@test "delete non existent context" {
|
||||
use_config config1
|
||||
|
||||
run ${COMMAND} -d "unknown-context"
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "delete several contexts" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} -d "user1@cluster1" "user2@cluster1"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" = "" ]]
|
||||
}
|
||||
|
||||
@test "delete several contexts including a non existent one" {
|
||||
use_config config2
|
||||
|
||||
run ${COMMAND} -d "user1@cluster1" "non-existent" "user2@cluster1"
|
||||
echo "$output"
|
||||
[ "$status" -eq 1 ]
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" = "user2@cluster1" ]]
|
||||
}
|
||||
144
test/kubens.bats
Normal file
144
test/kubens.bats
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
COMMAND="${BATS_TEST_DIRNAME}/../kubens"
|
||||
export KUBECTL="$BATS_TEST_DIRNAME/../test/mock-kubectl"
|
||||
|
||||
load common
|
||||
|
||||
@test "--help should not fail" {
|
||||
run ${COMMAND} --help
|
||||
echo "$output">&2
|
||||
[[ "$status" -eq 0 ]]
|
||||
}
|
||||
|
||||
@test "-h should not fail" {
|
||||
run ${COMMAND} -h
|
||||
echo "$output">&2
|
||||
[[ "$status" -eq 0 ]]
|
||||
}
|
||||
|
||||
@test "list namespaces when no kubeconfig exists" {
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[[ "$status" -eq 1 ]]
|
||||
[[ "$output" = *"current-context is not set"* ]]
|
||||
}
|
||||
|
||||
@test "list namespaces" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
|
||||
run ${COMMAND}
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
[[ "$output" = *"ns1"* ]]
|
||||
[[ "$output" = *"ns2"* ]]
|
||||
}
|
||||
|
||||
@test "switch to existing namespace" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
|
||||
run ${COMMAND} "ns1"
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
[[ "$output" = *'Active namespace is "ns1"'* ]]
|
||||
}
|
||||
|
||||
@test "switch to non-existing namespace" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
|
||||
run ${COMMAND} "unknown-namespace"
|
||||
echo "$output"
|
||||
[[ "$status" -eq 1 ]]
|
||||
[[ "$output" = *'no namespace exists with name "unknown-namespace"'* ]]
|
||||
}
|
||||
|
||||
@test "switch between namespaces" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
|
||||
run ${COMMAND} ns1
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
echo "$(get_namespace)"
|
||||
[[ "$(get_namespace)" = "ns1" ]]
|
||||
|
||||
run ${COMMAND} ns2
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
echo "$(get_namespace)"
|
||||
[[ "$(get_namespace)" = "ns2" ]]
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
echo "$(get_namespace)"
|
||||
[[ "$(get_namespace)" = "ns1" ]]
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
echo "$(get_namespace)"
|
||||
[[ "$(get_namespace)" = "ns2" ]]
|
||||
}
|
||||
|
||||
@test "switch to previous namespace when none exists" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[[ "$status" -eq 1 ]]
|
||||
[[ "$output" = *"No previous namespace found for current context"* ]]
|
||||
}
|
||||
|
||||
@test "switch to namespace when current context is empty" {
|
||||
use_config config1
|
||||
|
||||
run ${COMMAND} -
|
||||
echo "$output"
|
||||
[[ "$status" -eq 1 ]]
|
||||
[[ "$output" = *"current-context is not set"* ]]
|
||||
}
|
||||
|
||||
@test "-c/--current works when no namespace is set on context" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
|
||||
run ${COMMAND} "-c"
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
[[ "$output" = "default" ]]
|
||||
run ${COMMAND} "--current"
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
[[ "$output" = "default" ]]
|
||||
}
|
||||
|
||||
@test "-c/--current prints the namespace after it is set" {
|
||||
use_config config1
|
||||
switch_context user1@cluster1
|
||||
${COMMAND} ns1
|
||||
|
||||
run ${COMMAND} "-c"
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
[[ "$output" = "ns1" ]]
|
||||
run ${COMMAND} "--current"
|
||||
echo "$output"
|
||||
[[ "$status" -eq 0 ]]
|
||||
[[ "$output" = "ns1" ]]
|
||||
}
|
||||
|
||||
@test "-c/--current fails when current context is not set" {
|
||||
use_config config1
|
||||
run ${COMMAND} -c
|
||||
echo "$output"
|
||||
[[ "$status" -eq 1 ]]
|
||||
|
||||
run ${COMMAND} --current
|
||||
echo "$output"
|
||||
[[ "$status" -eq 1 ]]
|
||||
}
|
||||
12
test/mock-kubectl
Executable file
12
test/mock-kubectl
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
[[ -n $DEBUG ]] && set -x
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
if [[ $@ == *'get namespaces'* ]]; then
|
||||
echo "ns1"
|
||||
echo "ns2"
|
||||
else
|
||||
kubectl $@
|
||||
fi
|
||||
18
test/testdata/config1
vendored
Normal file
18
test/testdata/config1
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# config with one context
|
||||
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ""
|
||||
name: cluster1
|
||||
contexts:
|
||||
- context:
|
||||
cluster: cluster1
|
||||
user: user1
|
||||
name: user1@cluster1
|
||||
current-context: ""
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: user1
|
||||
user: {}
|
||||
24
test/testdata/config2
vendored
Normal file
24
test/testdata/config2
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# config with two contexts
|
||||
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ""
|
||||
name: cluster1
|
||||
contexts:
|
||||
- context:
|
||||
cluster: cluster1
|
||||
user: user1
|
||||
name: user1@cluster1
|
||||
- context:
|
||||
cluster: cluster1
|
||||
user: user2
|
||||
name: user2@cluster1
|
||||
current-context: ""
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: user1
|
||||
user: {}
|
||||
- name: user2
|
||||
user: {}
|
||||
11
utils.bash
11
utils.bash
@@ -1,11 +0,0 @@
|
||||
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}'
|
||||
}
|
||||
Reference in New Issue
Block a user