-
Notifications
You must be signed in to change notification settings - Fork 2
/
deploy
executable file
·371 lines (335 loc) · 9.6 KB
/
deploy
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#! /bin/bash
# Configurable parameters
# If the feature needs different parameters values, they can be set in
# the file {feature}/defaults, which is sourced a little later on. This
# defaults file SHOULD NOT TOUCH ANY OTHER VARIABLE than these, or hell
# may break loose.
TAR_OWNER=root:0
TAR_GROUP=root:0
DEPLOY_SSH=${DEPLOY_SSH:-ssh}
REMACCT=${REMACCT:+${REMACCT}\@}
# End of configurable parameters
here=${DEPLOY_DIR:-$(pwd)}
scriptdir=$(cd $(dirname $(realpath $0)); pwd)
if ! currentbranch=$(git rev-parse --abbrev-ref HEAD); then
echo >&2 "We ask the the configuration directory be git versioned"
exit 1
fi
save_args="$*"
upload=true
verbose=false
debug=false
while [ $# -gt 0 ]; do
case "$1" in
-n )
upload=false
;;
-v )
verbose=true
;;
-d )
debug=true
;;
* )
break
;;
esac
shift
done
feature=$1; shift
hosts="$*"
if ! tpage < /dev/null 2> /dev/null; then
cat >&2 <<EOF
Template Toolkit is required.
On a Debian based distribution, install libtemplate-perl
On a RPM base distribution, install perl-Template-Toolkit
(available through CPAN with the package Template)
EOF
exit 1
fi
if [ -z "$feature" ]; then
echo 'No feature name given'
exit 1
fi
if echo "$feature" | egrep -q '^_|[/[:space:]]'; then
echo 'Underscore (_), spaces and slashes (/) not permitted in feature names'
exit 1
fi
if cd $here/$feature; then
abshere=$(pwd)
tpagerc=
if [ -f tpagerc ]; then
tpagerc=$abshere/tpagerc
fi
if [ -f HOSTS -a -f README ]; then
allcurrenthosts=$(cat HOSTS \
| egrep -v '^(#|[[:space:]]*$)' \
| sed -e 's/^.*://' \
| grep -v '^\?' \
| sort \
| uniq)
maybecurrenthosts=$(cat HOSTS \
| egrep -v '^(#|[[:space:]]*$)' \
| sed -e 's/^.*://' \
| grep '^\?' \
| sed -e 's/^\?//' \
| sort \
| uniq)
for h in $allcurrenthosts; do
hh=${h//./\\.}
if expr "$maybecurrenthosts" : "^$hh\$" > /dev/null; then
echo >&2 "$h exists with and without ? in HOSTS"
exit 1
fi
done
if [ -z "$hosts" ]; then
hosts=$(echo "$allcurrenthosts"
for h in $maybecurrenthosts; do
if getent hosts $h > /dev/null; then
echo $h
fi
done)
else
if ! hosts=$(ok=/bin/true
for h in $hosts; do
hh=$h
hh_re=${hh//./\\.}
if ! ( echo $h | grep -q -e "\\." ); then
if hh=$((echo "$allcurrenthosts"
echo "$maybecurrenthosts") \
| grep -e "^$h\\.") \
&& [ $(echo "$hh" | wc -l) -gt 1 ]; then
echo >&2 "'$h' isn't unique enough"
ok=/bin/false
fi
elif ! (echo "$allcurrenthosts"
echo "$maybecurrenthosts") \
| grep -q -e "^$hh_re\$"; then
echo >&2 "'$h' isn't a staging host"
ok=/bin/false
fi
echo $hh
done
$ok); then
exit 1
fi
fi
declare -A fqdnhosts
fqdnhosts=()
for h in $(cat HOSTS); do
finalhost=${h%%:*}
currenthost=${h##*:}
currenthost=${currenthost##\?}
hh=${currenthost//./\\.}
if echo "$hosts" | grep -q -e "^$hh\$"; then
fqdnhosts[$currenthost]=$(echo ${fqdnhosts[$currenthost]} ${finalhost})
fi
done
if $debug; then
echo "DEBUG[fqdnhosts] collection of deployment hosts"
for currenthost in ${!fqdnhosts[*]}; do
echo "DEBUG[fqdnhosts] $currenthost => " ${fqdnhosts[$currenthost]}
done
fi
# Phase 0: Checks
echo >&2 "===== Phase 0: Checks"
if $upload; then
bail_out=/bin/false
defremtimeout=2
remtimeout=${REMTIMEOUT:-$defremtimeout}
for currenthost in ${!fqdnhosts[*]}; do
if ! $DEPLOY_SSH -o ConnectTimeout=$remtimeout $REMACCT$currenthost 'exit 0'; then
echo >&2 "Failed: ssh $REMACCT$currenthost"
bail_out=/bin/true
fi
done
if $bail_out; then
if [ -z "${REMACCT}" ]; then
echo >&2 "You may want to try setting REMACCT to your username on that host"
fi
echo >&2 "If the network connection to $currenthost is slow, you might want"
echo >&2 "to try setting REMTIMEOUT to a value higher than $remtimeout"
echo >&2 ""
echo >&2 " REMTIMEOUT=10 REMACCT=username $0 $save_args"
exit 1
fi
fi
staging=/tmp/deploy.$$
if [ -f defaults ]; then
. ./defaults
fi
if ! mkdir $staging; then
echo >&2 "Error: couldn't create directory $staging"
exit 2
fi
tpage_extra=""
for pp in $abshere/../preamble.tt2 $abshere/preamble.tt2; do
if [ -f "$pp" ]; then
tpage_extra="$tpage_extra --pre_process='$pp'"
fi
done
for pp in $abshere/postamble.tt2 $abshere/../postamble.tt2; do
if [ -f "$pp" ]; then
tpage_extra="$tpage_extra --post_process='$pp'"
fi
done
# Phase 1: Prepare files to be transfered
echo >&2 "===== Phase 1: Preparing files"
for currenthost in ${!fqdnhosts[*]}; do
data=$staging/$currenthost-data
control=$staging/$currenthost-control
tarball=$control/deploy.$feature.tar.gz
rm -f $tarball
if ! mkdir $data; then
echo >&2 "Error: couldn't create directory $data"
exit 2
fi
if ! mkdir $control; then
echo >&2 "Error: couldn't create directory $control"
exit 2
fi
tpage_cmd=$(echo tpage \
"--define hosts='${fqdnhosts[$currenthost]}'" \
"--define staginghost='$currenthost'" \
"--define feature='$feature'" \
"--define data='$data'" \
"--define control='$control'" \
"--define verbose='$verbose'" \
"--define scriptdir='$scriptdir'")
if [ -n "$tpagerc" ]; then
tpage_cmd="TPAGERC=$tpagerc $tpage_cmd"
fi
if $debug; then
echo "DEBUG[tpage_cmd] $tpage_cmd"
fi
# Phase 1.1: Run the pre-copy script
if [ -x pre-copy ]; then
( cd $data
HOSTS="${fqdnhosts[$currenthost]}" FEATURE="$feature" \
DATA="$data" CONTROL="$control" SOURCE="$abshere" \
VERBOSE="$verbose" \
$abshere/pre-copy )
fi
# Phase 1.2: Copy common files into staging directory
cp README $data/README.$feature
# Phase 1.3: Copy common source files into staging directory
if [ -d src ]; then
(
cd src;
git ls-files -z | xargs -0 tar -cf -
) | (
cd $data; tar -xpf -
)
fi
# Phase 1.4: Copy host specific files into staging directory
for h in $( for x in $currenthost ${fqdnhosts[$currenthost]}; do
echo $x
done | sort | uniq
for x in ${fqdnhosts[$currenthost]}; do
echo $x@$currenthost
done | sort | uniq ); do
if [ -d $h ]; then
(
cd $h;
git ls-files -z | xargs -0 tar -cf -
) | (
cd $data; tar -xpf -
)
fi
done
# Phase 1.5: process template source files in staging directory
# and replace them with their respective result
find $data -name '*.tt2' -type f | \
while read src; do
src=$(dirname "$src")/$(basename "$src")
dst=$(dirname "$src")/$(basename "$src" .tt2)
if [ "$src" != "$dst" ]; then
eval "$tpage_cmd $tpage_extra '$src'" > "$dst"
chmod --reference="$src" "$dst"
rm "$src"
fi
done
# Phase 1.7: Run post-copy
if [ -x post-copy ]; then
( cd $data
HOSTS="${fqdnhosts[$currenthost]}" FEATURE="$feature" \
DATA="$data" CONTROL="$control" SOURCE="$abshere" \
VERBOSE="$verbose" \
$abshere/post-copy )
fi
# Phase 1.8: Create the host specific pre-unpacking,
# post-unpacking and post-deployment scripts
for script in pre-unpack post-unpack post-deploy; do
if [ -f $script.tt2 ]; then
src=$script.tt2
dst=$control/$script.$feature
eval "$tpage_cmd $tpage_extra '$src'" > "$dst"
chmod a+x $dst
elif [ -x $script ]; then
dst=$control/$script.$feature
cp $script $dst
chmod a+x $dst
fi
done
# Phase 1.10: Create the local tarball and remove staging directory
(
cd $data
tar --owner=$TAR_OWNER --group=$TAR_GROUP -czf $tarball *
)
# Phase 1.20: Create the deployment scripts
for src in $scriptdir/_host_helpers/deploy*.tt2; do
dst=$control/$(basename $src .tt2).$feature
eval "$tpage_cmd $tpage_extra '$src'" > $dst
chmod a+x $dst
done
# Phase 1.21: Copy deploy-remove from REMOVE
if [ -f REMOVE ]; then
dst=$control/deploy-remove.$feature.txt
cp REMOVE $dst
fi
# Phase 1.99: Cleanup
rm -rf $data
done
if $upload; then
# Phase 2: Copy tarballs to intended hosts
echo >&2 "===== Phase 2: Copying files to remote hosts"
successhosts=""
for currenthost in ${!fqdnhosts[*]}; do
control=$staging/$currenthost-control
echo -n "Copying deployment tarball and deployment files to $currenthost..."
if scp -q -S $DEPLOY_SSH $control/* $REMACCT$currenthost:/tmp/; then
echo done
successhosts=$(echo $successhosts $currenthost)
else
echo FAIL
fi
rm -rf $control
done
# Phase 3: Final instructions
echo >&2 "===== Phase 3: Final instructions"
if [ -n "$successhosts" ]; then
tpage --define hosts="$successhosts" \
--define feature="$feature" \
--define scriptdir="$scriptdir" \
$scriptdir/_host_helpers/help.tt2
else
cat <<EOF
Copying failed on all hosts. Please fix the problem, or if someone else
was deploying at the same time as you, simply wait a moment
EOF
fi
rmdir $staging
else
echo >&2 "No upload selected, tarballs and deploy scripts preserved:"
for currenthost in ${!fqdnhosts[*]}; do
control=$staging/$currenthost-control
echo >&2
echo >&2 "${control}:"
ls $control >&2
done
fi
else
echo $feature lacking HOSTS and README
exit 1
fi
fi