[Libguestfs] [PATCH nbdkit 4/4] tests: Add a helper function which waits for nbdkit to start up.

Eric Blake eblake at redhat.com
Tue Sep 11 20:02:47 UTC 2018


On 9/11/18 1:47 PM, Richard W.M. Jones wrote:
> This assumes bashisms, but bash is required to run the tests.
> 
> This is mostly simple refactoring.  Except for the test-memory*.sh
> tests where nbdkit used to run in the foreground, but that seems to be
> a consequence of some left over debugging.
> ---

> +++ b/tests/functions.sh.in
> @@ -32,6 +32,41 @@
>   # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>   # SUCH DAMAGE.
>   
> +# start_nbdkit args...
> +#
> +# Run nbdkit with args and wait for it to start up.  If it fails
> +# to start up, exit with an error message.
> +start_nbdkit ()
> +{
> +    # Find the -P <pidfile> argument.
> +    pidfile=
> +    for i in `seq 1 $#`; do

Is seq universally available, even on BSD?  It's not in POSIX.  Since 
you're already using bashisms, you could instead do

for (( i=1; i <= $#; i++)); do

or even:

for i in {1..$#}; do

and drop the need for seq or even a fork (the former works regardless of 
$#, the latter has poor scaling effects if $# is large since it is 
expanded in advance - but then again, none of our tests pass that large 
of $#)

> +        if [ "${!i}" = "-P" ]; then

Unspecified behavior if ${!i} evaluates to "!" (which none of the tests 
do).  Since you're already using bashisms, you could instead do

if [[ ${!i} == -P ]]; then

> +            j=$((i+1))
> +            pidfile="${!j}"
> +        fi
> +    done

This parse loop requires tests to spell it "-P /path/to/file" as two 
separate args, rather than also permitting "-P/path/to/file".  A 
reasonable restriction but might be worth a comment.

> +    if [ "$pidfile" = "" ]; then
> +        echo "$0: start_nbdkit: -P option not present on nbdkit command line"
> +        exit 1
> +    fi
> +
> +    # Run nbdkit.
> +    nbdkit "$@"

You could also change the contract of this function to REQUIRE the pid 
filename be first, so that you don't have to hunt over $# where -P 
lives, something like:

start_nbdkit()
{
   pidfile=$1
   shift
   nbdkit -P $pidfile "$@"

and change callers to look like:

start_nbdkit foo.pid --myoption1 myplugin ...

> +
> +    # Wait for the pidfile to appear.
> +    for i in `seq 1 10`; do

I guess we were already relying on seq.  Could also be spelled {1..10}

> +        if test -f "$pidfile"; then
> +            break
> +        fi
> +        sleep 1
> +    done
> +    if ! test -f "$pidfile"; then
> +        echo "$0: start_nbdkit: PID file $pidfile was not created"
> +        exit 1
> +    fi
> +}
> +

> +++ b/tests/test-blocksize.sh
> @@ -31,6 +31,7 @@
>   # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>   # SUCH DAMAGE.
>   
> +source functions.sh
>   set -e

More need for source ./functions.sh throughout the patch

>   
>   files="blocksize1.img blocksize1.log blocksize1.sock blocksize1.pid
> @@ -72,27 +73,13 @@ cleanup ()
>   trap cleanup INT QUIT TERM EXIT ERR
>   
>   # Run two parallel nbdkit; to compare the logs and see what changes.
> -nbdkit -P blocksize1.pid -U blocksize1.sock \
> +start_nbdkit -P blocksize1.pid -U blocksize1.sock \
>          --filter=log file logfile=blocksize1.log blocksize1.img
> -nbdkit -P blocksize2.pid -U blocksize2.sock --filter=blocksize \
> +start_nbdkit -P blocksize2.pid -U blocksize2.sock --filter=blocksize \
>          --filter=log file logfile=blocksize2.log blocksize2.img \
>          minblock=1024 maxdata=512k maxlen=1M

If the first process starts but the second fails, then you have not set 
pid1=, and leak the first process.  This is a regression from the old 
code, which managed to capture pid1 before triggering the trap to 
cleanup(), and thus killed the successful nbdkit.  Several tests are 
impacted.

Less importantly, the old code performed startup in parallel (kick off 
both nbdkit processes, then start the loop; if a pid file was not 
produced by either one of the nbdkit processes, the test determined that 
within 10 seconds); now startup is serial (you don't start a second 
nbdkit until the first one has succeeded). For a test with two nbdkit 
processes, that could expand typical test time from 1 second to 2 (if 
the pid file creation is not instantaneous, but the first sleep 1 was 
sufficient for both processes to be ready); and expand worst-case 
failure detection under heavy load from 10 into 20 seconds (if the first 
succeeded on the last iteration, but the second timed out).  I don't see 
anything wrong with the change, although it might be worth a mention in 
the commit message as intentional.

I'm also seeing a common pattern of assigning pid=$(cat file.pid) right 
after calling start_nbdkt.  Would it be worth changing the signature of 
start_nbdkit() to also populate a pid variable on success, as in:

start_nbdkit blocksize1.pid pid1 myarg1

start_nbdkit()
{
   pidfile=$1
   pidvar=$2
   shift 2
   ...
   pid=$(cat $pidfile)
   eval $pidvar=$pid

which would also have the added benefit of avoiding the regression of 
pid1 not being set if the second nbdkit process doesn't start?

But overall, I like the idea of refactoring the common code for less 
repetition.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org




More information about the Libguestfs mailing list