# Common definitions for Rhizome test suites. # Copyright 2012-2014 Serval Project Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Some useful regular expressions. These must work for grep(1) (as basic # expressions) and also in sed(1). rexp_service='[A-Za-z0-9_]\+' rexp_manifestid='[0-9a-fA-F]\{64\}' rexp_bundlekey='[0-9a-fA-F]\{64\}' rexp_bundlesecret="$rexp_bundlekey" rexp_filehash='[0-9a-fA-F]\{128\}' rexp_filesize='[0-9]\{1,\}' rexp_tail='[0-9]\{1,\}' rexp_version='[0-9]\{1,\}' rexp_crypt='[01]' rexp_date='[0-9]\{1,\}' rexp_rowid='[0-9]\{1,\}' BID_NONEXISTENT=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF assert_manifest_complete() { local manifest="$1" tfw_cat -v "$manifest" assertGrep "$manifest" "^service=$rexp_service\$" assertGrep "$manifest" "^id=$rexp_manifestid\$" assertGrep "$manifest" "^date=$rexp_date\$" assertGrep "$manifest" "^version=$rexp_version\$" assertGrep "$manifest" "^filesize=$rexp_filesize\$" if $GREP -q '^filesize=0$' "$manifest"; then assertGrep --matches=0 "$manifest" "^filehash=" else assertGrep "$manifest" "^filehash=$rexp_filehash\$" fi if $GREP -q '^service=file$' "$manifest"; then assertGrep "$manifest" "^name=" fi } # Assertion function: # - assert that the output of a "servald rhizome list" command exactly describes the given files assert_rhizome_list() { assertStdoutIs --stderr --line=1 -e '13\n' assertStdoutIs --stderr --line=2 -e '_id:service:id:version:date:.inserttime:.author:.fromhere:filesize:filehash:sender:recipient:name\n' local exactly=true local re__inserttime="$rexp_date" local re__fromhere='[01]' local re__author="\($rexp_sid\)\{0,1\}" local files=0 local manifestname= local arg for arg; do case "$arg" in --fromhere=*) re__fromhere="${arg#*=}";; --author=*) re__author="${arg#*=}";; --manifest=*) manifestname="${arg#*=}";; --and-others) exactly=false;; --*) error "unsupported option: $arg";; *) unpack_manifest_for_grep "$arg" "$manifestname" assertStdoutGrep --stderr --matches=1 "^$rexp_rowid:$re_service:$re_manifestid:$re_version:$re_date:$re__inserttime:$re__author:$re__fromhere:$re_filesize:$re_filehash:$re_sender:$re_recipient:$re_name\$" let files+=1 manifestname= ;; esac done $exactly && assertStdoutLineCount --stderr '==' $(($files + 2)) rhizome_list_file_count=$(( $(replayStdout | wc -l) - 2 )) } rhizome_list_dump() { local ncols local -a headers local readvars= read ncols local oIFS="$IFS" IFS=: read -r -a headers IFS="$oIFS" local hdr local colvar for hdr in "${headers[@]}"; do readvars="$readvars __col_${hdr//[^A-Za-z0-9_]/_}" done eval local $readvars for colvar in $readvars; do eval "$colvar=ok" done local echoargs for colname; do case $colname in manifestid|bundleid) colvar=__col_id;; *) colvar="__col_${colname//[^A-Za-z0-9_]/_}";; esac eval [ -n "\"\${$colvar+ok}\"" ] || error "invalid column name: $colname" || return $? echoargs="$echoargs $colname=\"\${$colvar}\"" done IFS=: while eval read -r $readvars; do eval echo $echoargs done IFS="$oIFS" } assert_stdout_add_file() { local manifestname= while [ $# -gt 0 ]; do case "$1" in --manifest=*) manifestname="${1#*=}"; shift;; --*) error "unsupported option: $1"; return 1;; --) shift; break;; *) break;; esac done [ $# -ge 1 ] || error "missing filename arg" local filename="$1" shift unpack_manifest_for_grep "$filename" "$manifestname" actual_filesize=$(( $(cat "$filename" | wc -c) + 0 )) opt_service= opt_manifestid= opt_author= opt_secret= opt_BK= opt_filesize= opt_name=false if replayStdout | $GREP -q '^service:file$'; then opt_name=true fi fieldnames='service|manifestid|.author|.secret|BK|filesize|filehash|name' for arg; do case "$arg" in !+($fieldnames)) fieldname="${arg#!}" eval opt_${fieldname#.}=false ;; +($fieldnames)=*) value="${arg#*=}" fieldname="${arg%%=*}" assertStdoutGrep --matches=1 "^$fieldname:$value\$" eval opt_${fieldname#.}=false ;; *) error "unsupported argument: $arg" ;; esac done ${opt_service:-true} && assertStdoutGrep --matches=1 "^service:$re_service\$" ${opt_manifestid:-true} && assertStdoutGrep --matches=1 "^manifestid:$re_manifestid\$" ${opt_author:-true} && assertStdoutGrep --matches=1 "^\.author:$rexp_sid\$" ${opt_secret:-true} && assertStdoutGrep --matches=1 "^\.secret:$re_secret\$" ${opt_BK:-true} && assertStdoutGrep --matches=1 "^BK:$re_BK\$" ${opt_filesize:-true} && assertStdoutGrep --matches=1 "^filesize:$actual_filesize\$" if replayStdout | $GREP -q '^filesize:0$'; then assertStdoutGrep --matches=0 "^filehash:" fi ${opt_name:-true} && assertStdoutGrep --matches=1 "^name:$re_name\$" } assert_stdout_import_bundle() { # Output of "import bundle" is the same as "add file" but without the secret # or author fields. assert_stdout_add_file "$@" '!.secret' '!.author' } unpack_manifest_for_grep() { local filename="$1" local manifestname="${2:-$filename.manifest}" re_service="$rexp_service" re_manifestid="$rexp_manifestid" re_version="$rexp_version" re_date="$rexp_date" re_secret="$rexp_bundlesecret" re_BK="$rexp_bundlekey" re_sender="\($rexp_sid\)\{0,1\}" re_recipient="\($rexp_sid\)\{0,1\}" re_filesize="$rexp_filesize" re_filehash="\($rexp_filehash\)\{0,1\}" re_tail="$rexp_tail" re_name=$(escape_grep_basic "${filename##*/}") if [ -e "$manifestname" ]; then re_filesize=$($SED -n -e '/^filesize=/s///p' "$manifestname") if [ "$re_filesize" = 0 ]; then re_filehash= else re_filehash=$($SED -n -e '/^filehash=/s///p' "$manifestname") fi re_secret="$rexp_bundlesecret" re_service=$($SED -n -e '/^service=/s///p' "$manifestname") re_service=$(escape_grep_basic "$re_service") re_manifestid=$($SED -n -e '/^id=/s///p' "$manifestname") re_version=$($SED -n -e '/^version=/s///p' "$manifestname") re_tail=$($SED -n -e '/^tail=/s///p' "$manifestname") re_date=$($SED -n -e '/^date=/s///p' "$manifestname") re_crypt=$($SED -n -e '/^crypt=/s///p' "$manifestname") re_name=$($SED -n -e '/^name=/s///p' "$manifestname") re_name=$(escape_grep_basic "$re_name") re_BK=$($SED -n -e '/^BK=/s///p' "$manifestname") re_sender=$($SED -n -e '/^sender=/s///p' "$manifestname") re_recipient=$($SED -n -e '/^recipient=/s///p' "$manifestname") fi } assert_manifest_newer() { local manifest1="$1" local manifest2="$2" # The new manifest must have a higher version than the original. extract_manifest_version oldversion "$manifest1" extract_manifest_version newversion "$manifest2" assert [ $newversion -gt $oldversion ] # The new manifest must have a different filehash from the original. extract_manifest_filehash oldfilehash "$manifest1" extract_manifest_filehash newfilehash "$manifest2" assert [ $oldfilehash != $newfilehash ] } strip_signatures() { for file; do cat -v "$file" | $SED -e '/^^@/,$d' >"tmp.$file" && mv -f "tmp.$file" "$file" done } get_external_blob_path(){ local _var="$1" local _hash="$2" local _filepath="$SERVALINSTANCE_PATH/rhizome/blob/${_hash:0:2}/${_hash:2:2}/${_hash:4}" if [ -n "$_var" ]; then eval "$_var=\$_filepath" fi } extract_stdout_manifestid() { extract_stdout_keyvalue "$1" manifestid "$rexp_manifestid" } extract_stdout_version() { extract_stdout_keyvalue "$1" version "$rexp_version" } extract_stdout_author_optional() { extract_stdout_keyvalue_optional "$1" .author "$rexp_author" } extract_stdout_author() { extract_stdout_keyvalue "$1" .author "$rexp_author" } extract_stdout_secret_optional() { extract_stdout_keyvalue_optional "$1" .secret "$rexp_bundlesecret" } extract_stdout_secret() { extract_stdout_keyvalue "$1" .secret "$rexp_bundlesecret" } extract_stdout_rowid() { extract_stdout_keyvalue "$1" .rowid "$rexp_rowid" } extract_stdout_inserttime() { extract_stdout_keyvalue "$1" .inserttime "$rexp_date" } extract_stdout_BK() { extract_stdout_keyvalue "$1" BK "$rexp_bundlekey" } extract_stdout_date() { extract_stdout_keyvalue "$1" date "$rexp_date" } extract_stdout_filesize() { extract_stdout_keyvalue "$1" filesize "$rexp_filesize" } extract_stdout_filehash() { extract_stdout_keyvalue "$1" filehash "$rexp_filehash" } extract_stdout_tail() { extract_stdout_keyvalue "$1" tail "$rexp_tail" } extract_stdout_crypt() { extract_stdout_keyvalue "$1" crypt "$rexp_crypt" } extract_manifest() { local __var="$1" local __manifestfile="$2" local __label="$3" local __rexp="${4:-[^=]*}" local __value=$($SED -n -e "/^$__label=$__rexp\$/s/^$__label=//p" "$__manifestfile") [ -n "$__var" ] && eval $__var="\$__value" } assert_manifest_fields() { local manifestfile="$1" shift assert --message="manifest file $manifestfile is readable" [ -r "$manifestfile" ] [ $# -gt 0 ] || error "missing arguments" local arg label value for arg; do case "$arg" in !*) assertGrep \ --matches=0 \ --message="$manifestfile contains no '$arg=' line" \ --dump-on-fail="$manifestfile" \ "$manifestfile" "^$arg=" ;; *=*) label="${arg%%=*}" value="${arg#*=}" local mvalue extract_manifest mvalue "$manifestfile" "$label" assert \ --message="$manifestfile contains '$label=$value' line" \ --dump-on-fail="$manifestfile" \ [ "$mvalue" = "$value" ] ;; *) assertGrep \ --message="$manifestfile contains '$arg=' line" \ --dump-on-fail="$manifestfile" \ "$manifestfile" "^$arg=" ;; esac done } extract_manifest_service() { extract_manifest "$1" "$2" service "$rexp_service" } extract_manifest_id() { extract_manifest "$1" "$2" id "$rexp_manifestid" } extract_manifest_BK() { extract_manifest "$1" "$2" BK "$rexp_bundlekey" } extract_manifest_filesize() { extract_manifest "$1" "$2" filesize "$rexp_filesize" } extract_manifest_tail() { extract_manifest "$1" "$2" tail "$rexp_tail" } extract_manifest_filehash() { extract_manifest "$1" "$2" filehash "$rexp_filehash" } extract_manifest_name() { extract_manifest "$1" "$2" name ".*" } extract_manifest_version() { extract_manifest "$1" "$2" version "$rexp_version" } extract_manifest_date() { extract_manifest "$1" "$2" date "$rexp_date" } extract_manifest_crypt() { extract_manifest "$1" "$2" crypt "$rexp_crypt" } rhizome_http_server_started() { local logvar=LOG${1#+} $GREP 'HTTP SERVER START.*port=[0-9].*services=[^ ]*\' "${!logvar}" } get_rhizome_server_port() { get_servald_http_server_port "$@" } # Predicate function: # - return true if the file bundles identified by args BID[:VERSION] has been # received by all the given instances args +I # - does this by examining the server log files of the respective instances # for tell-tale INFO messages bundle_received_by() { local -a rexps bundles local restart=true local arg bid version rexp bundle bundlefile i local ret=0 for arg; do case "$arg" in +([0-9A-F])?(:+([0-9]))) $restart && rexps=() bundles=() restart=false bid="${arg%%:*}" matches_rexp "$rexp_manifestid" "$bid" || error "invalid bundle ID: $bid" || return $? bundles+=("$arg") if [ "$bid" = "$arg" ]; then rexps+=("RHIZOME ADD MANIFEST service=.* bid=$bid") else version="${arg#*:}" rexps+=("RHIZOME ADD MANIFEST service=.* bid=$bid version=$version") fi ;; +[A-Z]) tfw_nolog push_and_set_instance $arg || return $? tfw_nolog assert_servald_server_status running for ((i = 0; i < ${#bundles[*]}; ++i)); do bundle="${bundles[$i]}" rexp="${rexps[$i]}" bundledir="$instance_dir/cache/bundles_received" bundlefile="$bundledir/$bundle" if [ ! -s "$bundlefile" ]; then [ -d "$bundledir" ] || mkdir -p "$bundledir" || error "mkdir failed" if grep "$rexp" "$instance_servald_log" >"$bundlefile"; then tfw_log "bundle $bundle received by instance +$instance_name" else ret=1 fi fi done restart=true tfw_nolog pop_instance ;; --stderr) for ((i = 0; i < ${#bundles[*]}; ++i)); do bundle="${bundles[$i]}" rexp="${rexps[$i]}" if replayStderr | grep "$rexp" >/dev/null; then tfw_log "bundle $bundle received by ($executed)" else ret=1 fi done restart=true ;; *) error "invalid argument: $arg" return 1 ;; esac done return $ret } extract_manifest_vars() { local manifest="${1?}" extract_manifest_id BID "$manifest" extract_manifest_version VERSION "$manifest" extract_manifest_filesize FILESIZE "$manifest" FILEHASH= if [ "$FILESIZE" != '0' ]; then extract_manifest_filehash FILEHASH "$manifest" fi } rhizome_add_file() { local name="$1" local size="${2:-64}" rhizome_add_files --size="$size" "$name" extract_manifest_vars "$name.manifest" } rhizome_add_files() { local size=64 local sidvar="SID${instance_name}1" local -a names=() for arg; do case "$arg" in --size=*) size="${arg##*=}" ;; *) local name="$arg" [ -e "$name" ] || create_file "$name" $size executeOk_servald rhizome add file "${!sidvar}" "$name" "$name.manifest" names+=("$name") esac done executeOk_servald rhizome list assert_rhizome_list --fromhere=1 --author="${!sidvar}" "${names[@]}" --and-others } rhizome_update_file() { local orig_name="$1" local new_name="$2" [ -e "$new_name" ] || echo 'File $new_name' >"$new_name" local sidvar="SID${instance_name}1" [ "$new_name" != "$orig_name" ] && cp "$orig_name.manifest" "$new_name.manifest" $SED -i -e '/^date=/d;/^filehash=/d;/^filesize=/d;/^version=/d;/^name=/d' "$new_name.manifest" executeOk_servald rhizome add file "${!sidvar}" "$new_name" "$new_name.manifest" executeOk_servald rhizome list assert_rhizome_list --fromhere=1 "$new_name" extract_manifest_vars "$new_name.manifest" } assert_rhizome_received() { [ $# -ne 0 ] || error "missing arguments" local name local _id for name; do if [ -s "$name" ]; then extract_manifest_id _id "$name.manifest" executeOk_servald rhizome extract file "$_id" extracted assert cmp "$name" extracted fi done } RHIZOME_USE_RESTFUL=false rhizome_use_restful() { setup_curl 7 RHIZOME_USE_RESTFUL=true RHIZOME_RESTFUL_USER="$1" RHIZOME_RESTFUL_PASSWORD="$2" get_servald_restful_http_server_port RHIZOME_RESTFUL_PORT } # Extract a value from an HTTP header extract_http_header() { local __var="$1" local __headerfile="$2" local __header="$3" local __rexp="$4" local __value=$($SED -n -e "/^$__header:[ $HT]*$__rexp$CR\$/s/^$__header:[ $HT]*\(.*\)$CR\$/\1/p" "$__headerfile") assert --message="$__headerfile contains valid '$__header' header" \ --dump-on-fail="$__headerfile" \ [ -n "$__value" ] [ -n "$__var" ] && eval $__var=\"\$__value\" } # Parse an HTTP quoted-string http_unquote_string() { local __var="$1" local __unq="$(eval echo '"${'$__var'}"' | sed -e 's/^"//' -e 's/"$//' -e 's/\\\(.\)/\1/g')" eval $__var=\"\$__unq\" } rhizome_add_bundles() { local encrypted=false case "$1" in --encrypted) encrypted=true; shift;; esac local SID="${1?}" shift local n for ((n = $1; n <= $2; ++n)); do create_file file$n $((1000 + $n)) >file$n.manifest if $encrypted; then echo "crypt=1" >>file$n.manifest fi if $RHIZOME_USE_RESTFUL; then executeOk curl \ -H "Expect:" \ --silent --fail --show-error \ --output "file$n.manifest" \ --dump-header http.header$n \ --basic --user "$RHIZOME_RESTFUL_USER:$RHIZOME_RESTFUL_PASSWORD" \ --form "bundle-author=$SID" \ --form "manifest=@file$n.manifest;type=rhizome/manifest;format=\"text+binarysig\"" \ --form "payload=@file$n" \ "http://$addr_localhost:$RHIZOME_RESTFUL_PORT/restful/rhizome/insert" tfw_cat --stderr http.header$n -v file$n.manifest extract_http_header BID[$n] http.header$n Serval-Rhizome-Bundle-Id "$rexp_manifestid" extract_http_header VERSION[$n] http.header$n Serval-Rhizome-Bundle-Version "$rexp_version" extract_http_header SIZE[$n] http.header$n Serval-Rhizome-Bundle-Filesize "$rexp_filesize" extract_http_header HASH[$n] http.header$n Serval-Rhizome-Bundle-Filehash "$rexp_filehash" extract_http_header DATE[$n] http.header$n Serval-Rhizome-Bundle-Date "$rexp_date" extract_http_header BK[$n] http.header$n Serval-Rhizome-Bundle-BK "$rexp_bundlekey" extract_http_header ROWID[$n] http.header$n Serval-Rhizome-Bundle-Rowid "$rexp_rowid" extract_http_header AUTHOR[$n] http.header$n Serval-Rhizome-Bundle-Author "$rexp_sid" extract_http_header SECRET[$n] http.header$n Serval-Rhizome-Bundle-Secret "$rexp_bundlesecret" extract_http_header INSERTTIME[$n] http.header$n Serval-Rhizome-Bundle-Inserttime "$rexp_date" if $encrypted; then extract_http_header CRYPT[$n] http.header$n Serval-Rhizome-Bundle-Crypt "$rexp_crypt" else CRYPT[$n]= fi else executeOk_servald rhizome add file "$SID" file$n file$n.manifest extract_stdout_manifestid BID[$n] extract_stdout_version VERSION[$n] extract_stdout_filesize SIZE[$n] extract_stdout_filehash HASH[$n] extract_stdout_date DATE[$n] extract_stdout_BK BK[$n] extract_stdout_rowid ROWID[$n] extract_stdout_author AUTHOR[$n] extract_stdout_secret SECRET[$n] extract_stdout_inserttime INSERTTIME[$n] if $encrypted; then extract_stdout_crypt CRYPT[$n] else CRYPT[$n]= fi fi NAME[$n]=file$n if $encrypted; then assert [ "${CRYPT[$n]}" = 1 ] fi executeOk_servald rhizome export file "${HASH[$n]}" raw$n if $encrypted; then assert ! cmp file$n raw$n else assert cmp file$n raw$n fi [ "${ROWID[$n]}" -gt "${ROWID_MAX:-0}" ] && ROWID_MAX=${ROWID[$n]} done } rhizome_delete_payload_blobs() { local filehash for filehash; do get_external_blob_path blob_path "$filehash" assert --message="Rhizome external blob file exists, filehash=$filehash" [ -e "$blob_path" ] rm -f "$blob_path" done }