Skip to content

Commit 74ea664

Browse files
committed
Initial commit
0 parents  commit 74ea664

File tree

4 files changed

+316
-0
lines changed

4 files changed

+316
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/output/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2020 Wenhao Ji <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
NAME=kubectl-tmux-exec
2+
VERSION=0.0.1
3+
4+
OUTPUT_DIR=output
5+
RELEASE_FILE_NAME=$(NAME)-$(VERSION).tar.gz
6+
RELEASE_FILE_PATH=$(OUTPUT_DIR)/$(RELEASE_FILE_NAME)
7+
SIG_FILE_NAME=$(NAME)-$(VERSION).asc
8+
SIG_FILE_PATH=$(OUTPUT_DIR)/$(SIG_FILE_NAME)
9+
10+
all: $(RELEASE_FILE_PATH) $(SIG_FILE_PATH)
11+
12+
mk-output-dir:
13+
mkdir -p $(OUTPUT_DIR)
14+
15+
$(RELEASE_FILE_PATH): mk-output-dir
16+
tar czvf $(RELEASE_FILE_PATH) bin/ LICENSE
17+
18+
build: $(RELEASE_FILE_PATH)
19+
20+
$(SIG_FILE_PATH): $(RELEASE_FILE_PATH)
21+
gpg -ab $(RELEASE_FILE_PATH)
22+
23+
sign: $(SIG_FILE_PATH)
24+
25+
clean:
26+
rm -rf $(RELEASE_FILE_PATH)
27+
28+
.PHONY: build sign clean mk-output-dir

bin/kubectl-tmux-exec

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2020 Wenhao Ji <[email protected]>
4+
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
# this software and associated documentation files (the "Software"), to deal in
7+
# the Software without restriction, including without limitation the rights to
8+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
# the Software, and to permit persons to whom the Software is furnished to do so,
10+
# subject to the following conditions:
11+
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
set -euf -o pipefail
23+
24+
readonly PROG_NAME='kubectl tmux exec'
25+
26+
declare -ra KUBECTL_SHORT_OPTS=(
27+
'n'
28+
's'
29+
)
30+
31+
declare -ra KUBECTL_LONG_OPTS=(
32+
'container'
33+
'cluster'
34+
'context'
35+
'namespace'
36+
'password'
37+
'request-timeout'
38+
'server'
39+
'token'
40+
'user'
41+
'username'
42+
)
43+
44+
declare -ra KUBECTL_NOARG_LONG_OPTS=(
45+
'insecure-skip-tls-verify'
46+
)
47+
48+
declare -ra TMUX_LAYOUTS=(
49+
'even-horizontal'
50+
'even-vertical'
51+
'main-horizontal'
52+
'main-vertical'
53+
'tiled'
54+
)
55+
56+
function usage() {
57+
cat << EOF
58+
Execute a command in all containers that match the label selector using Tmux.
59+
60+
Examples:
61+
# Keep tracking nginx access logs by running 'tail' command from all pods that match the selector 'app=nginx',
62+
# using the first container by default
63+
${PROG_NAME} -l app=nginx -- tail -f /var/log/nginx/access.log
64+
65+
# Open bash terminals that attach to 'nginx' containers of all nginx pods
66+
${PROG_NAME} -l app=nginx -c nginx -it /bin/bash
67+
68+
Options:
69+
-c, --container='': Container name. If omitted, the first container in the pod will be chosen
70+
-i, --stdin=false: Pass stdin to the container
71+
-t, --tty=false: Stdin is a TTY
72+
-l, --selector: Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)
73+
--remain-on-exit=false: Remain Tmux window on exit
74+
--select-layout=tiled: one of the five Tmux preset layouts: even-horizontal, even-vertical, main-horizontal,
75+
main-vertical, or tiled.
76+
77+
Usage:
78+
${PROG_NAME} -l label [-c CONTAINER] [flags] -- COMMAND [args...]
79+
80+
Use "kubectl options" for a list of global command-line options (applies to all commands).
81+
EOF
82+
}
83+
84+
function check_required_executables() {
85+
for exe in "$@"; do
86+
if ! which "${exe}" 2>&1 >/dev/null; then
87+
echo >&2 "command not found: ${exe}"
88+
exit 127
89+
fi
90+
done
91+
}
92+
93+
function error_and_exit() {
94+
echo >&2 'error:' "$@"
95+
echo >&2 "Run '${PROG_NAME} --help' for more information on the command."
96+
exit 1
97+
}
98+
99+
function ggetopt() {
100+
if [[ ! -z "${GNU_GETOPT_PREFIX:-}" ]]; then
101+
"${GNU_GETOPT_PREFIX}/bin/getopt" "$@"
102+
return
103+
fi
104+
105+
local getopt_test=0
106+
getopt -T 2>&1 >/dev/null || getopt_test="$?"
107+
if [[ "${getopt_test}" -eq 4 ]]; then
108+
getopt "$@"
109+
return
110+
fi
111+
echo >&2 'The getopt is not GNU enhanced version.'
112+
echo >&2 'Please install gnu-getopt and either add it your PATH or set GNU_GETOPT_PREFIX env variable to its installed location.'
113+
exit 4
114+
}
115+
116+
function array_contains() {
117+
local occur="$1"
118+
local arr=("${@:2}")
119+
for e in "${arr[@]}"; do
120+
if [[ "${e}" == "${occur}" ]]; then
121+
return 0
122+
fi
123+
done
124+
return 1
125+
}
126+
127+
function main() {
128+
local opts
129+
opts=$(ggetopt -o hitc:l:"$(printf '%s:' "${KUBECTL_SHORT_OPTS[@]}")" --long \
130+
help,stdin,tty,container:,selector:,remain-on-exit,select-layout:,"$(printf '%s:,' "${KUBECTL_LONG_OPTS[@]}")","$(printf '%s,' "${KUBECTL_NOARG_LONG_OPTS[@]}")" -- "$@")
131+
eval set -- $opts
132+
133+
local selector
134+
local container_name
135+
local opt_stdin=0
136+
local opt_tty=0
137+
local kubectl_opts=()
138+
local remain_on_exit=0
139+
local tmux_layout='tiled'
140+
while [[ $# -gt 0 ]]; do
141+
local opt="$1"
142+
case "${opt}" in
143+
-h|--help)
144+
usage
145+
exit 0
146+
;;
147+
-c|--container)
148+
shift
149+
container_name="$1"
150+
;;
151+
-i|--stdin)
152+
opt_stdin=1
153+
;;
154+
-t|--tty)
155+
opt_tty=1
156+
;;
157+
-l|--selector)
158+
shift
159+
selector="$1"
160+
;;
161+
--remain-on-exit)
162+
remain_on_exit=1
163+
;;
164+
--select-layout)
165+
shift
166+
tmux_layout="$1"
167+
;;
168+
--)
169+
shift
170+
break
171+
;;
172+
-*)
173+
if [[ "${#opt}" -eq 2 ]]; then
174+
if array_contains "${opt:1}" "${KUBECTL_NOARG_SHORT_OPTS[@]}"; then
175+
kubectl_opts+=("${opt}")
176+
elif array_contains "${opt:1}" "${KUBECTL_SHORT_OPTS[@]}"; then
177+
shift
178+
kubectl_opts+=("${opt}" "$1")
179+
else
180+
break
181+
fi
182+
else
183+
if array_contains "${opt:2}" "${KUBECTL_NOARG_LONG_OPTS[@]}"; then
184+
kubectl_opts+=("${opt}")
185+
elif array_contains "${opt:2}" "${KUBECTL_LONG_OPTS[@]}"; then
186+
shift
187+
kubectl_opts+=("${opt}" "$1")
188+
else
189+
break
190+
fi
191+
fi
192+
;;
193+
*)
194+
break
195+
;;
196+
esac
197+
shift
198+
done
199+
200+
check_required_executables 'kubectl' 'tmux'
201+
202+
if [[ $# -eq 0 ]]; then
203+
error_and_exit 'you must specify at least one command for the container'
204+
fi
205+
206+
if [[ -z "${selector:-}" ]]; then
207+
error_and_exit 'The label selector option is required: -l'
208+
fi
209+
210+
if [[ -z "${tmux_layout}" ]] || ! array_contains "${tmux_layout}" "${TMUX_LAYOUTS[@]}"; then
211+
error_and_exit "Unknown layout: ${tmux_layout}"
212+
fi
213+
214+
local commands=("$@")
215+
216+
local kubectl_exec_opts=''
217+
if [[ "${opt_stdin}" -eq 1 ]]; then
218+
kubectl_exec_opts="${kubectl_exec_opts} -i"
219+
fi
220+
if [[ "${opt_tty}" -eq 1 ]]; then
221+
kubectl_exec_opts="${kubectl_exec_opts} -t"
222+
fi
223+
if [[ ! -z "${container_name:-}" ]]; then
224+
kubectl_exec_opts="${kubectl_exec_opts} -c ${container_name}"
225+
fi
226+
227+
local exec_cmd_str=''
228+
for arg in "${commands[@]}"; do
229+
if [[ -z "${exec_cmd_str}" ]]; then
230+
exec_cmd_str="$(printf '%q' "${arg}")"
231+
else
232+
exec_cmd_str="${exec_cmd_str} $(printf '%q' "${arg}")"
233+
fi
234+
done
235+
236+
local pods=()
237+
while IFS='' read -r pod_name; do
238+
pods+=("${pod_name}")
239+
done < <(
240+
kubectl ${kubectl_opts[@]:-} get pods -l "${selector}" -o custom-columns=':metadata.name' --no-headers
241+
)
242+
243+
if [[ "${#pods[@]}" -eq 0 ]]; then
244+
echo >&2 'No pods found.'
245+
exit 0
246+
fi
247+
248+
local tmux_commands=()
249+
for pod in "${pods[@]}"; do
250+
local cmd="kubectl ${kubectl_opts[@]:-} exec ${kubectl_exec_opts} ${pod} -- ${exec_cmd_str}"
251+
if [[ "${#tmux_commands[@]}" -eq 0 ]]; then
252+
tmux_commands+=('new-session' "${cmd}" ';')
253+
else
254+
tmux_commands+=('split-window' "${cmd}" ';')
255+
fi
256+
done
257+
258+
if [[ "${remain_on_exit}" -eq 1 ]]; then
259+
tmux_commands+=('set-option' 'remain-on-exit' 'on' ';')
260+
fi
261+
tmux_commands+=('select-layout' "${tmux_layout}" ';' 'setw' 'synchronize-panes' 'on' ';')
262+
263+
exec tmux "${tmux_commands[@]}"
264+
}
265+
266+
main "$@"

0 commit comments

Comments
 (0)