challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

test-challenger-db-retry.sh (4098B)


      1 #!/usr/bin/env bash
      2 # This file is in the public domain.
      3 #
      4 # Regression test for finding 5: '/setup' must survive a serialization
      5 # failure instead of answering 500.
      6 #
      7 # The database session is SERIALIZABLE (see challengerdb/pg.c), so every
      8 # single-statement query is its own serializable transaction.  The first
      9 # statement of 'do_insert_validation' is
     10 #   UPDATE clients SET validation_counter=validation_counter+1 ...
     11 # which every /setup of a given client performs on the *same* row.  Any
     12 # concurrent writer of that row therefore makes /setup fail with SQLSTATE
     13 # 40001 -> GNUNET_DB_STATUS_SOFT_ERROR, which the handler must retry.
     14 #
     15 # We provoke exactly that, deterministically: a psql session holds an open
     16 # transaction that has already updated the counter row, /setup is issued
     17 # (and blocks on the row lock), and the holder then commits.  PostgreSQL
     18 # then aborts the blocked statement with 40001.  With the retry loop the
     19 # request succeeds on the next attempt; without it the user gets a 500.
     20 
     21 set -eu
     22 
     23 # Exit, with status code "skip" (no 'real' failure)
     24 function exit_skip() {
     25     echo " SKIP: $1"
     26     exit 77
     27 }
     28 
     29 # Exit, with error message (hard failure)
     30 function exit_fail() {
     31     echo " FAIL: $@"
     32     exit 1
     33 }
     34 
     35 # Cleanup to run whenever we exit
     36 function cleanup()
     37 {
     38     exec 3>&- 2> /dev/null || true
     39     for n in $(jobs -p)
     40     do
     41         kill $n 2> /dev/null || true
     42     done
     43     rm -f "$LAST_RESPONSE" "$BLOCKER_FIFO"
     44     wait
     45 }
     46 
     47 LAST_RESPONSE=$(mktemp responseXXXXXX.log)
     48 BLOCKER_FIFO=$(mktemp -u blockerXXXXXX.fifo)
     49 
     50 # Install cleanup handler (except for kill -9)
     51 trap cleanup EXIT
     52 
     53 export PATH="$PATH:."
     54 
     55 echo -n "Testing for jq"
     56 jq -h > /dev/null || exit_skip "jq required"
     57 echo " FOUND"
     58 echo -n "Testing for curl"
     59 curl -h > /dev/null || exit_skip "curl required"
     60 echo " FOUND"
     61 echo -n "Testing for wget"
     62 wget -h > /dev/null || exit_skip "wget required"
     63 echo " FOUND"
     64 echo -n "Testing for psql"
     65 psql --version > /dev/null || exit_skip "psql required"
     66 echo " FOUND"
     67 echo -n "Testing for challenger-httpd ..."
     68 challenger-httpd -h > /dev/null || exit_skip "challenger-httpd required"
     69 echo " FOUND"
     70 
     71 CONF="test-challenger.conf"
     72 BURL="http://localhost:9967"
     73 REDIRECT_URI="http://client.example.com/"
     74 
     75 DB_URI=$(challenger-config -c "${CONF}" -s challengerdb-postgres -o CONFIG)
     76 
     77 echo -n "Initialize challenger database ..."
     78 challenger-dbinit -r -c "${CONF}" &> dbinit.log
     79 echo " OK"
     80 
     81 echo -n "Add challenger client ..."
     82 CLIENT_SECRET="secret-token:secret"
     83 challenger-admin -c "${CONF}" -a "${CLIENT_SECRET}" "${REDIRECT_URI}" &> admin.log
     84 echo " OK"
     85 # We just reset the DB, thus the client ID must be 1 here:
     86 CLIENT_ID=1
     87 
     88 echo -n "Start challenger-httpd ..."
     89 challenger-httpd -L INFO -c "${CONF}" &> httpd.log &
     90 
     91 # Wait for challenger to be available
     92 for n in $(seq 1 50)
     93 do
     94     echo -n "."
     95     sleep 0.2
     96     OK=0
     97     wget --tries=1 --timeout=1 "${BURL}/config" -o /dev/null -O /dev/null >/dev/null || continue
     98     OK=1
     99     break
    100 done
    101 if [ 1 != $OK ]
    102 then
    103     exit_skip "Failed to launch challenger service"
    104 fi
    105 echo " OK"
    106 
    107 echo -n "Locking the client's validation counter ..."
    108 mkfifo "${BLOCKER_FIFO}"
    109 psql "${DB_URI}" -q -f "${BLOCKER_FIFO}" &> blocker.log &
    110 exec 3>"${BLOCKER_FIFO}"
    111 echo "BEGIN; UPDATE challenger.clients SET validation_counter=validation_counter+1 WHERE client_serial_id=${CLIENT_ID};" >&3
    112 sleep 1
    113 echo " OK"
    114 
    115 echo -n "Setup new validation process against the lock ..."
    116 curl "${BURL}/setup/${CLIENT_ID}" \
    117     -H "Authorization: Bearer ${CLIENT_SECRET}" \
    118     -d '' \
    119     -w "%{http_code}" -s -o $LAST_RESPONSE > status.txt &
    120 CURL_PID=$!
    121 # Give the request time to reach the UPDATE and block on the row lock,
    122 # then release it; the blocked statement now fails with 40001.
    123 sleep 2
    124 echo "COMMIT;" >&3
    125 exec 3>&-
    126 wait $CURL_PID
    127 STATUS=$(cat status.txt)
    128 rm -f status.txt
    129 
    130 if [ "$STATUS" != "200" ]
    131 then
    132     exit_fail "Expected 200 OK after retrying the serialization failure. Got: $STATUS" $(cat $LAST_RESPONSE)
    133 fi
    134 NONCE=$(jq -r .nonce < "$LAST_RESPONSE")
    135 if [ "x$NONCE" = "xnull" ]
    136 then
    137     exit_fail "Expected a nonce in the /setup response"
    138 fi
    139 echo " OK"
    140 
    141 exit 0