4 log
"In the updates subdirectory of the directory this script is in,"
5 log
"there are a bunch of config files. You should call this script,"
6 log
"passing the names of one or more of those files as parameters"
9 log
"This will validate that the update.xml files all exist for the"
10 log
"given config file, and that they report the correct file sizes"
11 log
"for the associated mar files, and that the associated mar files"
12 log
"are available on the update servers."
14 log
"This script will spawn multiple curl processes to query the"
15 log
"snippets (update.xml file downloads) and the download urls in"
16 log
"parallel. The number of parallel curl processes can be managed"
17 log
"with the -p MAX_PROCS option."
19 log
"Only the first three bytes of the mar files are downloaded"
20 log
"using curl -r 0-2 option to save time. GET requests are issued"
21 log
"rather than HEAD requests, since Akamai (one of our CDN"
22 log
"partners) caches GET and HEAD requests separately - therefore"
23 log
"they can be out-of-sync, and it is important that we validate"
24 log
"that the GET requests return the expected results."
26 log
"Please note this script can run on linux and OS X. It has not"
27 log
"been tested on Windows, but may also work. It can be run"
28 log
"locally, and does not require access to the mozilla vpn or"
29 log
"any other special network, since the update servers are"
30 log
"available over the internet. However, it does require an"
31 log
"up-to-date checkout of the tools repository, as the updates/"
32 log
"subfolder changes over time, and reflects the currently"
33 log
"available updates. It makes no changes to the update servers"
34 log
"so there is no harm in running it. It simply generates a"
35 log
"report. However, please try to avoid hammering the update"
36 log
"servers aggressively, e.g. with thousands of parallel"
37 log
"processes. For example, feel free to run the examples below,"
38 log
"first making sure that your source code checkout is up-to-"
39 log
"date on your own machine, to get the latest configs in the"
40 log
"updates/ subdirectory."
43 log
" $(basename "${0}") [-p MAX_PROCS] config1 [config2 config3 config4 ...]"
44 log
" $(basename "${0}") -h"
47 log
" 1. $(basename "${0}") -p 128 mozBeta-thunderbird-linux.cfg mozBeta-thunderbird-linux64.cfg"
48 log
" 2. $(basename "${0}") mozBeta-thunderbird-linux64.cfg"
55 # subprocesses don't log in real time, due to synchronisation
56 # issues which can cause log entries to overwrite each other.
57 # therefore this function outputs log entries written to
58 # temporary files on disk, and then deletes them.
60 ls -1rt "${TMPDIR}" |
grep '^log\.' |
while read LOG
62 cat "${TMPDIR}/${LOG}"
67 # this function takes an update.xml url as an argument
68 # and then logs a list of config files and their line
69 # numbers, that led to this update.xml url being tested
70 function show_cfg_file_entries
{
71 local update_xml_url
="${1}"
72 cat "${update_xml_urls}" | cut
-f1 -d' ' |
grep -Fn "${update_xml_url}" |
sed 's/:.*//' |
while read match_line_no
74 cfg_file
="$(sed -n -e "${match_line_no}p
" "${update_xml_urls}" | cut -f3 -d' ')"
75 cfg_line_no
="$(sed -n -e "${match_line_no}p
" "${update_xml_urls}" | cut -f4 -d' ')"
76 log
" ${cfg_file} line ${cfg_line_no}: $(sed -n -e "${cfg_line_no}p" "${cfg_file}")"
80 # this function takes a mar url as an argument and then
81 # logs information about which update.xml urls referenced
82 # this mar url, and which config files referenced those
83 # mar urls - so you have a full understanding of why this
84 # mar url was ever tested
85 function show_update_xml_entries {
87 grep -Frl "${mar_url}" "${TMPDIR}" | grep '/update_xml_to_mar\.' | while read update_xml_to_mar
89 mar_size="$
(cat "${update_xml_to_mar}" | cut
-f2 -d' ')"
90 update_xml_url="$
(cat "${update_xml_to_mar}" | cut
-f3 -d' ')"
91 patch_type="$
(cat "${update_xml_to_mar}" | cut
-f4 -d' ')"
92 update_xml_actual_url="$
(cat "${update_xml_to_mar}" | cut
-f5 -d' ')"
93 log " ${update_xml_url}"
94 [ -n "${update_xml_actual_url}" ] && log " which redirected to
: ${update_xml_actual_url}"
95 log " This contained an entry
for:"
96 log " patch type: ${patch_type}"
97 log " mar size
: ${mar_size}"
98 log " mar url
: ${mar_url}"
99 log " The update.xml url above was retrieved because of the following cfg
file entries
:"
100 show_cfg_file_entries "${update_xml_url}" | sed 's/ / /'
104 echo -n "$
(date): Command called
:"
105 for ((INDEX=0; INDEX<=$#; INDEX+=1))
107 echo -n " '${!INDEX}'"
110 log "From directory
: '$(pwd)'"
112 log "Parsing arguments...
"
114 # Max procs lowered in bug 894368 to try to avoid spurious failures
118 while getopts p:h OPT
121 p) MAX_PROCS="${OPTARG}";;
127 shift "$
((OPTIND
- 1))"
129 # invalid option specified
130 [ "${BAD_ARG}" == 1 ] && exit 66
132 log "Checking one or
more config files have been specified...
"
136 log "ERROR
: You must specify one or
more config files
"
140 log "Checking whether MAX_PROCS is a number...
"
141 if ! let x=MAX_PROCS 2>/dev/null
144 log "ERROR
: MAX_PROCS must be a number
(-p option
); you specified
'${MAX_PROCS}' - this is not a number.
"
148 # config files are in updates subdirectory below this script
149 if ! cd "$
(dirname "${0}")/updates
" 2>/dev/null
151 log "ERROR
: Cannot
cd into
'$(dirname "${0}")/updates' from
'$(pwd)'"
155 log "Checking specified config files
(and downloading them
if necessary
):"
160 if [[ ${file} == http* ]]
162 log " Downloading config
file '${file}'"
164 curl -fL --retry 5 --compressed "${file}" > "$cfg"
165 if [ "$?
" != 0 ]; then
166 log "Error downloading config
file '${file}'"
169 log " * '${file}' ok
, downloaded to
'${cfg}'"
172 elif [ -f "${file}" ]
174 log " * '${file}' ok
"
177 log " * '${file}' missing
"
183 # invalid config specified
184 if [ "${BAD_FILE}" == 1 ]
186 log "ERROR
: Unable to download config
file(s
) or config files are missing from repo
- see above
"
190 log "All checks completed successfully.
"
192 log "Starting stopwatch...
"
194 log "Please be aware output will now be buffered up
, and only displayed after completion.
"
195 log "Therefore
do not be alarmed
if you see no output
for several minutes.
"
196 log "See https
://bugzilla.mozilla.org
/show_bug.cgi?id
=863602#c5 for details".
199 START_TIME
="$(date +%s)"
201 # Create a temporary directory for all temp files, that can easily be
202 # deleted afterwards. See https://bugzilla.mozilla.org/show_bug.cgi?id=863602
203 # to understand why we write everything in distinct temporary files rather
204 # than writing to standard error/standard out or files shared across
206 # Need to unset TMPDIR first since it affects mktemp behaviour on next line
208 export TMPDIR
="$(mktemp -d -t final_verification.XXXXXXXXXX)"
210 # this temporary file will list all update urls that need to be checked, in this format:
211 # <update url> <comma separated list of patch types> <cfg file that requests it> <line number of config file>
213 # https://aus4.mozilla.org/update/3/Firefox/18.0/20130104154748/Linux_x86_64-gcc3/zh-TW/releasetest/default/default/default/update.xml?force=1 complete moz20-firefox-linux64-major.cfg 3
214 # https://aus4.mozilla.org/update/3/Firefox/18.0/20130104154748/Linux_x86_64-gcc3/zu/releasetest/default/default/default/update.xml?force=1 complete moz20-firefox-linux64.cfg 7
215 # https://aus4.mozilla.org/update/3/Firefox/19.0/20130215130331/Linux_x86_64-gcc3/ach/releasetest/default/default/default/update.xml?force=1 complete,partial moz20-firefox-linux64-major.cfg 11
216 # https://aus4.mozilla.org/update/3/Firefox/19.0/20130215130331/Linux_x86_64-gcc3/af/releasetest/default/default/default/update.xml?force=1 complete,partial moz20-firefox-linux64.cfg 17
217 update_xml_urls
="$(mktemp -t update_xml_urls.XXXXXXXXXX)"
219 ####################################################################################
220 # And now a summary of all temp files that will get generated during this process...
222 # 1) mktemp -t failure.XXXXXXXXXX
224 # Each failure will generate a one line temp file, which is a space separated
225 # output of the error code, and the instance data for the failure.
228 # PATCH_TYPE_MISSING https://aus4.mozilla.org/update/3/Firefox/4.0b12/20110222205441/Linux_x86-gcc3/dummy-locale/releasetest/update.xml?force=1 complete https://aus4.mozilla.org/update/3/Firefox/4.0b12/20110222205441/Linux_x86-gcc3/dummy-locale/releasetest/default/default/default/update.xml?force=1
230 # 2) mktemp -t update_xml_to_mar.XXXXXXXXXX
232 # For each mar url referenced in an update.xml file, a temp file will be created to store the
233 # association between update.xml url and mar url. This is later used (e.g. in function
234 # show_update_xml_entries) to trace back the update.xml url(s) that led to a mar url being
235 # tested. It is also used to keep a full list of mar urls to test.
238 # <mar url> <mar size> <update.xml url> <patch type> <update.xml redirection url, if HTTP 302 returned>
240 # 3) mktemp -t log.XXXXXXXXXX
242 # For each log message logged by a subprocesses, we will create a temp log file with the
243 # contents of the log message, since we cannot safely output the log message from the subprocess
244 # and guarantee that it will be correctly output. By buffering log output in individual log files
245 # we guarantee that log messages will not interfere with each other. We then flush them when all
246 # forked subprocesses have completed.
248 # 4) mktemp -t mar_headers.XXXXXXXXXX
250 # We keep a copy of the mar url http headers retrieved in one file per mar url.
252 # 5) mktemp -t update.xml.headers.XXXXXXXXXX
254 # We keep a copy of the update.xml http headers retrieved in one file per update.xml url.
256 # 6) mktemp -t update.xml.XXXXXXXXXX
258 # We keep a copy of each update.xml file retrieved in individual files.
259 ####################################################################################
262 # generate full list of update.xml urls, followed by patch types,
263 # as defined in the specified config files - and write into "${update_xml_urls}" file
264 aus_server
="https://aus5.mozilla.org"
265 for cfg_file
in "${configs[@]}"
268 sed -e 's/localtest/cdntest/' "${cfg_file}" |
while read config_line
271 # to avoid contamination between iterations, reset variables
272 # each loop in case they are not declared
273 # aus_server is not "cleared" each iteration - to be consistent with previous behaviour of old
274 # final-verification.sh script - might be worth reviewing if we really want this behaviour
275 release
="" product
="" platform
="" build_id
="" locales
="" channel
="" from
="" patch_types
="complete"
276 eval "${config_line}"
277 for locale
in ${locales}
279 echo "${aus_server}/update/3/$product/$release/$build_id/$platform/$locale/$channel/default/default/default/update.xml?force=1" "${patch_types// /,}" "${cfg_file}" "${line_no}"
282 done > "${update_xml_urls}"
284 # download update.xml files and grab the mar urls from downloaded file for each patch type required
285 cat "${update_xml_urls}" | cut
-f1-2 -d' ' |
sort -u |
xargs -n2 "-P${MAX_PROCS}" ..
/get-update-xml.sh
286 if [ "$?" != 0 ]; then
288 log
"Error generating update requests"
294 # download http header for each mar url
295 find "${TMPDIR}" -name 'update_xml_to_mar.*' -type f |
xargs cat | cut
-f1-2 -d' ' |
sort -u |
xargs -n2 "-P${MAX_PROCS}" ..
/test-mar-url.sh
296 if [ "$?" != 0 ]; then
298 log
"Error HEADing mar urls"
305 log
'Stopping stopwatch...'
306 STOP_TIME
="$(date +%s)"
308 number_of_failures
="$(find "${TMPDIR}" -name 'failure.*' -type f | wc -l | sed 's/ //g')"
309 number_of_update_xml_urls
="$(cat "${update_xml_urls}" | cut -f1 -d' ' | sort -u | wc -l | sed 's/ //g')"
310 number_of_mar_urls
="$(find "${TMPDIR}" -name "update_xml_to_mar.
*" | xargs cat | cut -f1 -d' ' | sort -u | wc -l | sed 's/ //g')"
312 if [ "${number_of_failures}" -eq 0 ]
315 log
"All tests passed successfully."
320 log
'===================================='
321 [ "${number_of_failures}" -gt 1 ] && log
"${number_of_failures} FAILURES" || log
'1 FAILURE'
323 ls -1tr "${TMPDIR}" |
grep '^failure\.' |
while read failure_file
325 while read failure_code entry1 entry2 entry3 entry4 entry5 entry6 entry7
327 log
'===================================='
329 case "${failure_code}" in
331 UPDATE_XML_UNAVAILABLE
)
332 update_xml_url
="${entry1}"
333 update_xml
="${entry2}"
334 update_xml_headers
="${entry3}"
335 update_xml_debug
="${entry4}"
336 update_xml_curl_exit_code
="${entry5}"
337 log
"FAILURE $((++failure)): Update xml file not available"
339 log
" Download url: ${update_xml_url}"
340 log
" Curl returned exit code: ${update_xml_curl_exit_code}"
342 log
" The HTTP headers were:"
343 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml_headers}"
345 log " The full curl debug output was:"
346 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml_debug}"
348 log
" The returned update.xml file was:"
349 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml}"
351 log " This url was tested because of the following cfg file entries:"
352 show_cfg_file_entries "${update_xml_url}"
357 UPDATE_XML_REDIRECT_FAILED)
358 update_xml_url="${entry1}"
359 update_xml_actual_url="${entry2}"
360 update_xml="${entry3}"
361 update_xml_headers="${entry4}"
362 update_xml_debug="${entry5}"
363 update_xml_curl_exit_code="${entry6}"
364 log "FAILURE $((++failure)): Update xml file not available at *redirected* location"
366 log " Download url: ${update_xml_url}"
367 log " Redirected to: ${update_xml_actual_url}"
368 log " It could not be downloaded from this url."
369 log " Curl returned exit code: ${update_xml_curl_exit_code}"
371 log " The HTTP headers were:"
372 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml_headers}"
374 log
" The full curl debug output was:"
375 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml_debug}"
377 log " The returned update.xml file was:"
378 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml}"
380 log
" This url was tested because of the following cfg file entries:"
381 show_cfg_file_entries
"${update_xml_url}"
386 update_xml_url
="${entry1}"
387 patch_type
="${entry2}"
388 update_xml
="${entry3}"
389 update_xml_headers
="${entry4}"
390 update_xml_debug
="${entry5}"
391 update_xml_actual_url
="${entry6}"
392 log
"FAILURE $((++failure)): Patch type '${patch_type}' not present in the downloaded update.xml file."
394 log
" Update xml file downloaded from: ${update_xml_url}"
395 [ -n "${update_xml_actual_url}" ] && log
" This redirected to the download url: ${update_xml_actual_url}"
396 log
" Curl returned exit code: 0 (success)"
398 log
" The HTTP headers were:"
399 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml_headers}"
401 log " The full curl debug output was:"
402 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml_debug}"
404 log
" The returned update.xml file was:"
405 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${update_xml}"
407 log " This url and patch type combination was tested due to the following cfg file entries:"
408 show_cfg_file_entries "${update_xml_url}"
414 mar_headers_file="${entry2}"
415 mar_headers_debug_file="${entry3}"
416 mar_file_curl_exit_code="${entry4}"
417 mar_actual_url="${entry5}"
418 log "FAILURE $((++failure)): Could not retrieve mar file"
420 log " Mar file url: ${mar_url}"
421 [ -n "${mar_actual_url}" ] && log " This redirected to: ${mar_actual_url}"
422 log " The mar file could not be downloaded from this location."
423 log " Curl returned exit code: ${mar_file_curl_exit_code}"
425 log " The HTTP headers were:"
426 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${mar_headers_file}"
428 log
" The full curl debug output was:"
429 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${mar_headers_debug_file}"
431 log " The mar download was tested because it was referenced in the following update xml file(s):"
432 show_update_xml_entries "${mar_url}"
438 mar_required_size="${entry2}"
439 mar_actual_size="${entry3}"
440 mar_headers_file="${entry4}"
441 mar_headers_debug_file="${entry5}"
442 mar_file_curl_exit_code="${entry6}"
443 mar_actual_url="${entry7}"
444 log "FAILURE $((++failure)): Mar file is wrong size"
446 log " Mar file url: ${mar_url}"
447 [ -n "${mar_actual_url}" ] && log " This redirected to: ${mar_actual_url}"
448 log " The http header of the mar file url says that the mar file is ${mar_actual_size} bytes."
449 log " One or more of the following update.xml file(s) says that the file should be ${mar_required_size} bytes."
451 log " These are the update xml file(s) that referenced this mar:"
452 show_update_xml_entries "${mar_url}"
454 log " Curl returned exit code: ${mar_file_curl_exit_code}"
456 log " The HTTP headers were:"
457 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${mar_headers_file}"
459 log
" The full curl debug output was:"
460 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${mar_headers_debug_file}"
464 BAD_HTTP_RESPONSE_CODE_FOR_MAR)
466 mar_headers_file="${entry2}"
467 mar_headers_debug_file="${entry3}"
468 mar_file_curl_exit_code="${entry4}"
469 mar_actual_url="${entry5}"
470 http_response_code="$(sed -e "s/$(printf '\r')//" -n -e '/^HTTP\
//p
' "${mar_headers_file}" | tail -1)"
471 log "FAILURE $((++failure)): '${http_response_code}' for mar file"
473 log " Mar file url: ${mar_url}"
474 [ -n "${mar_actual_url}" ] && log " This redirected to: ${mar_actual_url}"
476 log " These are the update xml file(s) that referenced this mar:"
477 show_update_xml_entries "${mar_url}"
479 log " Curl returned exit code: ${mar_file_curl_exit_code}"
481 log " The HTTP headers were:"
482 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${mar_headers_file}"
484 log
" The full curl debug output was:"
485 sed -e "s/$(printf '\r')//" -e "s/^/$(date): /" -e '$a\' "${mar_headers_debug_file}"
490 log "ERROR: Unknown failure code - '${failure_code}'"
491 log "ERROR: This is a serious bug in this script."
492 log "ERROR: Only known failure codes are: UPDATE_XML_UNAVAILABLE, UPDATE_XML_REDIRECT_FAILED, PATCH_TYPE_MISSING, NO_MAR_FILE, MAR_FILE_WRONG_SIZE, BAD_HTTP_RESPONSE_CODE_FOR_MAR"
494 log "FAILURE $((++failure)): Data from failure is: ${entry1} ${entry2} ${entry3} ${entry4} ${entry5} ${entry6}"
499 done < "${TMPDIR}/${failure_file}"
506 log '===================================='
508 log '===================================='
510 log "Config files scanned: ${#@}"
511 log "Update xml files downloaded and parsed: ${number_of_update_xml_urls}"
512 log "Unique mar urls found: ${number_of_mar_urls}"
513 log "Failures: ${number_of_failures}"
514 log "Parallel processes used (maximum limit): ${MAX_PROCS}"
515 log "Execution time: $((STOP_TIME-START_TIME)) seconds"