-
Notifications
You must be signed in to change notification settings - Fork 0
/
run-command.sh
executable file
·101 lines (88 loc) · 2.91 KB
/
run-command.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env bash
set -e
ME=$(basename "$0")
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DATA_FILE="${DIR}/output.json"
HOST_IP_FILE="${DIR}/ips"
declare -a DEFAULT_FILTERS=('Name=instance-state-name,Values=running')
declare -a REQUIRED_TOOLS=('dsh' 'jq')
logOut() {
echo -e "[${ME}] $(date +%Y-%m-%dT%H:%M:%S%z) | ${1}"
}
failErrOut() {
echo "[${ME}] $(date +%Y-%m-%dT%H:%M:%S%z) | ERROR - ${1} ! Exiting ..." >&2
exit 1
}
printHelp() {
echo "Usage: ${ME} [-l dsh_fork_limit] -f '<filter 1>' -f '<filter 2>' ... -c '<command>'" >&2
echo
echo " -l dsh_fork_limit If not set, command is executed on one node at a time."
echo " -f filter One or more aws describe-instance filters. https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options"
echo " -c command Command that is executed on the nodes."
echo
echo " Example:"
echo " ./${ME} -l 2 -f 'Name=tag:Environment,Values=loadtest' -f 'Name=tag:Cluster,Values=dash,id,exp' -c 'echo Test'"
echo
}
checkInput() {
local regexp_str="^Name=[a-zA-Z0-9.\-\:]+,Values=[a-zA-Z0-9.\-]+(,[a-zA-Z0-9.\-]+)*$"
for tool in "${REQUIRED_TOOLS[@]}"; do
command -v $tool >/dev/null 2>&1 || failErrOut "'${tool}' is not installed ..."
done
[[ -z $CMD ]] && failErrOut "Command must be set using '-c' option"
[[ -z $FILTERS ]] && failErrOut "Filters must be set using '-f' option"
for filter in "${FILTERS[@]}"; do
[[ ! $filter =~ $regexp_str ]] && failErrOut "Filter '${filter}' is wrong"
done
return 0
}
prepareExecution() {
aws ec2 describe-instances --filters $(echo ${DEFAULT_FILTERS[@]}) $(echo ${FILTERS[@]}) --query 'Reservations[].Instances[].{PrivateIpAddress:PrivateIpAddress,Name:Tags[?Key==`Name`].Value}' --output json > $DATA_FILE
if [[ "$(cat $DATA_FILE)" = "[]" ]]; then
logOut "No instance found. Exiting ..."
exit 0
fi
logOut "Execute '${CMD}' on the following nodes:\n\n$(cat $DATA_FILE | jq '.[] | "\(.Name) \(.PrivateIpAddress)"' | sed 's/"//g')\n"
read -p "Continue (y/n)? " choice
echo
case "$choice" in
y|Y ) logOut "Executing ...\n" && cat $DATA_FILE | jq '.[].PrivateIpAddress' | sed 's/"//g' > $HOST_IP_FILE;;
n|N ) doCleanup && logOut "Exiting ..." && exit 0 ;;
* ) failErrOut "Invalid choice '${choice}'" ;;
esac
}
runCommand() {
if [[ -z $DSH_FORK_LIMIT ]]; then
dsh_opts="-M"
else
dsh_opts="-F ${DSH_FORK_LIMIT} -c -M"
fi
dsh $dsh_opts -f $HOST_IP_FILE $CMD
}
doCleanup() {
rm -f $DATA_FILE $HOST_IP_FILE 2>/dev/null
}
while getopts ":h?f:l:c:" opt; do
case $opt in
h)
printHelp
exit 0 ;;
f) FILTERS+=("$OPTARG") ;;
c) CMD=$OPTARG ;;
l) DSH_FORK_LIMIT=$OPTARG ;;
:)
failErrOut "Option '${OPTARG}' requires an argument"
;;
esac
done
if [ $OPTIND -eq 1 ]; then
printHelp
exit 0
fi
shift $((OPTIND -1))
checkInput
prepareExecution
runCommand
doCleanup
logOut ""
logOut "Done"