Skip to content

Commit 66d3432

Browse files
committed
feat: added imqctl, imqlog tools
1 parent c968add commit 66d3432

File tree

5 files changed

+474
-171
lines changed

5 files changed

+474
-171
lines changed

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,47 @@ imq completions off
167167

168168
Currently it supports both `zsh` and `bash` shells.
169169

170+
## Controlling Local Services
171+
172+
For comfortable local development @imqueue provides couple of useful
173+
command-line tools, allowing developers to manage local set of services.
174+
Like starting/stopping/restarting them with a single command line or managing
175+
services logs.
176+
177+
Please, note, there are many different ways to manage local services.
178+
You may consider pulling and starting pre-build docker images, or even
179+
use docker compose for managing them, or may utilize such tools as
180+
vagrant to organize local environment setup. BTW, you may suggest to
181+
run your services locally on host OS, which is really useful scenario
182+
during development and the tools below will dramatically improve your
183+
experience, especially, when the number of services to manage significant.
184+
185+
### imqctl
186+
187+
~~~
188+
Usage: imqctl <command> [-p path] [-s services] [-hu]
189+
<command> is one of start|stop|restart
190+
[-p path] - path to a directory with services repositories, by default is
191+
current directory
192+
[-s services] - comma-separated services list (repositories names),
193+
if not passed will scan path for a services presence
194+
[-u] - if passed service will be updated using 'git pull' before start
195+
[-c] - calm down services start - wait before staring next
196+
[-v] - verbose mode, shows command execution time
197+
[-h] - print this usage information
198+
~~~
199+
200+
### imqlog
201+
202+
~~~
203+
Usage: ./bin/log.sh [-c] [service1, ...serviceN]
204+
[service1, ...serviceN] - list of service repositories directories names to
205+
combile logs for, if omitted all existing logs are
206+
combined.
207+
[-c] - clean previous logs
208+
[-h] - print this usage information
209+
~~~
210+
170211
## License
171212

172213
[ISC](https://github.com/imqueue/cli/blob/master/LICENSE)

bin/ctl.sh

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
#!/bin/bash
2+
# Copyright (c) 2019, imqueue.com <[email protected]>
3+
#
4+
# Permission to use, copy, modify, and/or distribute this software for any
5+
# purpose with or without fee is hereby granted, provided that the above
6+
# copyright notice and this permission notice appear in all copies.
7+
#
8+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9+
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10+
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11+
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12+
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13+
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
# PERFORMANCE OF THIS SOFTWARE.
15+
program="$0 $*"
16+
cwd=$(pwd)
17+
workdir="$HOME/.imq/var"
18+
pidfile="$workdir/.pids"
19+
argv=()
20+
path="."
21+
services=()
22+
update=0
23+
calm=0
24+
verbose=0
25+
warnmsg=""
26+
27+
function wait_service {
28+
local svc="$1"
29+
local logfile="$workdir/$svc.log"
30+
local file errfile
31+
32+
file=$(grep 'reader channel connected' "$logfile")
33+
34+
if [[ -z "$file" ]]; then
35+
errfile=$(grep 'UnhandledPromiseRejectionWarning:' "$logfile")
36+
37+
if [[ -z "$errfile" ]]; then
38+
sleep 1
39+
wait_service "$svc"
40+
else
41+
warnmsg="warn: service $svc errored, please, consider watching logs..."
42+
fi
43+
fi
44+
}
45+
46+
function start_services {
47+
local dir="$1"
48+
local pid svc
49+
50+
if [[ ! -d "$dir" ]]; then
51+
echo "No such directory: $dir" >&2
52+
exit 1
53+
fi
54+
55+
for svc in "${services[@]}"; do
56+
cd "$dir/$svc" || exit 1
57+
58+
if [[ 1 -eq $update ]]; then
59+
echo "Updating $svc..."
60+
git pull || exit 1
61+
fi
62+
63+
printf "Starting %s" "$svc"
64+
65+
if [[ $(npm run --silent dev &> "$workdir/$svc.log") ]] & pid="$!"; then
66+
:
67+
fi
68+
69+
if [[ 1 -eq "$calm" ]]; then
70+
wait_service "$svc"
71+
fi
72+
73+
printf ", master pid is %d...\n" $pid
74+
touch "$pidfile"
75+
echo "$svc:$pid" >> "$pidfile"
76+
77+
if [[ -n "$warnmsg" ]]; then
78+
echo "$warnmsg"
79+
warnmsg=""
80+
fi
81+
82+
cd "$cwd" || exit 1
83+
done
84+
85+
if [[ 0 -eq "$calm" ]]; then
86+
echo "Bulk service start initiated, please, be patient..."
87+
fi
88+
}
89+
90+
function pids_list {
91+
local pid="$1"
92+
local pids child_pid
93+
94+
if [[ -z "$pid" ]]; then
95+
return 0
96+
fi
97+
98+
echo "$1"
99+
mapfile -t pids < <(ps --ppid "$pid" -o pid | awk 'NR>1')
100+
101+
if [[ ! 0 -eq "${#pids[@]}" ]]; then
102+
for child_pid in "${pids[@]}"; do
103+
pids_list "$child_pid"
104+
done
105+
fi
106+
}
107+
108+
function stop_services {
109+
local dir="$1"
110+
local pids present svc svc_name pid parts info
111+
112+
if [[ -f "$pidfile" ]]; then
113+
while IFS= read -r info; do
114+
IFS=':' read -r -a parts <<< "$info"
115+
svc_name="${parts[0]}"
116+
pid="${parts[1]}"
117+
present=$(in_services "$svc_name")
118+
119+
if [[ 0 -eq $present ]]; then
120+
echo "$svc_name:$pid" >> "${pidfile}.lock"
121+
continue
122+
fi
123+
124+
mapfile -t pids < <(pids_list "$pid")
125+
126+
echo "Stopping ${svc_name} by pids ${pids[*]}"
127+
kill -s TERM "${pids[@]}" &> /dev/null
128+
done < "$pidfile"
129+
130+
rm "$pidfile"
131+
132+
if [[ -f "${pidfile}.lock" ]]; then
133+
mv "${pidfile}.lock" "$pidfile"
134+
fi
135+
fi
136+
137+
for svc in "${services[@]}"; do
138+
cd "$dir/$svc" || exit 1
139+
140+
if [[ $(npm run --silent stop) ]] &> /dev/null; then
141+
:
142+
fi
143+
144+
cd "$cwd" || exit 1
145+
done
146+
}
147+
148+
function usage {
149+
echo "Usage: $0 <command> [-p path] [-s services] [-hu]" >&2
150+
echo " <command> is one of start|stop|restart" >&2
151+
echo " [-p path] - path to a directory with services repositories, by default is current directory" >&2
152+
echo " [-s services] - comma-separated services list (repositories names), if not passed will scan path for a services presence" >&2
153+
echo " [-u] - if passed service will be updated using 'git pull' before start" >&2
154+
echo " [-c] - calm down services start - wait before staring next" >&2
155+
echo " [-v] - verbose mode, shows command execution time" >&2
156+
echo " [-h] - print this usage information" >&2
157+
}
158+
159+
function in_services {
160+
local service_name="$1"
161+
local service_list_lookup=" ${services[*]} "
162+
local service_name_lookup=" ${service_name} "
163+
164+
if [[ "$service_list_lookup" =~ $service_name_lookup ]]; then
165+
echo 1
166+
else
167+
echo 0
168+
fi
169+
}
170+
171+
# parse command-line args
172+
while [ $# -gt 0 ]; do
173+
unset OPTIND
174+
unset OPTARG
175+
176+
while getopts hucvp:s: options; do
177+
case $options in
178+
p) path="$OPTARG" ;;
179+
s) IFS=',' read -ra services <<< "$OPTARG" ;;
180+
h) usage ; exit 0 ;;
181+
u) update=1 ;;
182+
c) calm=1 ;;
183+
v) verbose=1 ;;
184+
\?|*) usage ; exit 1 ;;
185+
esac
186+
done
187+
188+
shift $((OPTIND-1))
189+
argv+=("$1")
190+
shift
191+
done
192+
193+
# load services from path if they were not provided by command-line option
194+
if [[ 0 -eq "${#services[@]}" ]]; then
195+
mapfile -t service_entries < <(find \
196+
"$path"/*/src \
197+
-type f \
198+
-name "*.ts" \
199+
-exec grep -lP 'extends\s+IMQ(Service|Client)\s*\{' {} +)
200+
201+
for file in "${service_entries[@]}"; do
202+
IFS='/' read -ra file_path <<< "${file#${path}/}"
203+
service_name="${file_path[0]}"
204+
present=$(in_services "$service_name")
205+
206+
if [[ 0 -eq $present ]]; then
207+
services+=( "$service_name" )
208+
fi
209+
done
210+
fi
211+
212+
# make sure workdir exists to store pids
213+
if [[ ! -d "$workdir" ]]; then
214+
mkdir -p "$workdir" || exit 1
215+
fi
216+
217+
# check if command passed
218+
if [[ -z "${argv[0]}" ]]; then
219+
echo "Command expected, but not given!" >&2
220+
usage
221+
exit 1
222+
fi
223+
224+
# do the job
225+
start=$(date +%s)
226+
227+
case "${argv[0]}" in
228+
start)
229+
(start_services "$path")
230+
;;
231+
stop)
232+
(stop_services "$path")
233+
;;
234+
restart)
235+
(stop_services "$path")
236+
(start_services "$path")
237+
;;
238+
esac
239+
240+
end=$(date +%s)
241+
runtime=$((end - start))
242+
243+
if [[ 1 -eq "$verbose" ]]; then
244+
echo "Command '$program' executed in ${runtime} sec."
245+
fi

bin/log.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/bash
2+
# Copyright (c) 2019, imqueue.com <[email protected]>
3+
#
4+
# Permission to use, copy, modify, and/or distribute this software for any
5+
# purpose with or without fee is hereby granted, provided that the above
6+
# copyright notice and this permission notice appear in all copies.
7+
#
8+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9+
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10+
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11+
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12+
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13+
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
# PERFORMANCE OF THIS SOFTWARE.
15+
workdir="$HOME/.imq/var"
16+
argv=()
17+
18+
function multitail {
19+
trap 'kill $(jobs -p) &> /dev/null' EXIT
20+
21+
args=( "$@" )
22+
23+
if [[ -z "${args[*]}" ]]; then
24+
mapfile -t logs < <(find \
25+
"$workdir" \
26+
-type f \
27+
-name "*.log")
28+
29+
for logfile in "${logs[@]}"; do
30+
tail -f -n +1 "$logfile" &
31+
done
32+
else
33+
for svc in "${args[@]}"; do
34+
logfile="$workdir/$svc.log"
35+
36+
if [[ -f "$logfile" ]]; then
37+
tail -f -n +1 "$logfile" &
38+
else
39+
echo "warn: log-file for service $svc has not been found"
40+
fi
41+
done
42+
fi
43+
44+
wait
45+
}
46+
47+
function usage {
48+
echo "Usage: $0 [-c] [service1, ...serviceN]" >&2
49+
echo " [service1, ...serviceN] - list of service repositories directories names to combile logs for, if omitted all existing logs are combined."
50+
echo " [-c] - clean previous logs" >&2
51+
echo " [-h] - print this usage information" >&2
52+
}
53+
54+
# parse command-line args
55+
while [ $# -gt 0 ]; do
56+
unset OPTIND
57+
unset OPTARG
58+
59+
while getopts hc options; do
60+
case $options in
61+
h) usage ; exit 0 ;;
62+
c) find "$workdir" -type f -name "*.log" -delete ;;
63+
\?|*) usage ; exit 1 ;;
64+
esac
65+
done
66+
67+
shift $((OPTIND-1))
68+
argv+=("$1")
69+
shift
70+
done
71+
72+
multitail "${argv[@]}"

0 commit comments

Comments
 (0)