Testing Singularity builds

A quick post on testing Singularity builds, where I discovered how the %test section of the Singularity Definition File works. If you aren't familiar, the Definition File is similar to the Dockerfile for Docker; it is used to define and build the image. From the documentation:

A SingularityCE Definition File (or “def file” for short) is like a set of blueprints explaining how to build a custom container. It includes specifics about the base OS to build or the base container to start from, as well as software to install, environment variables to set at runtime, files to add from the host system, and container metadata.

There is a test section, which as described in the documentation:

The %test section runs at the very end of the build process, and can be used to validate the container using methods of your choosing. You can also execute this scriptlet through the container itself, using the test command.

Here's an example from the documentation

%test
    grep -q NAME=\"Ubuntu\" /etc/os-release
    if [ $? -eq 0 ]; then
        echo "Container base is Ubuntu as expected."
    else
        echo "Container base is not Ubuntu."
        exit 1
    fi

However, it wasn't obvious to me that this section will not fail even with a failing command. For example, the following def file will build.

Bootstrap: docker
From: debian:bullseye-slim

%runscript
    exec /usr/local/bin/hello.sh

%post
    printf '#!/usr/bin/env bash\necho Hi there\n' > /usr/local/bin/hello.sh \
    && chmod 755 /usr/local/bin/hello.sh

%test
    laksdjf
    echo $?
    /usr/local/bin/hello.sh
INFO:    Starting build...
INFO:    Running post scriptlet
+ printf #!/usr/bin/env bash\necho Hi there\n
+ chmod 755 /usr/local/bin/hello.sh
INFO:    Adding runscript
INFO:    Adding testscript
INFO:    Running testscript
/.singularity.d/test: 3: laksdjf: not found
127
Hi there
INFO:    Creating SIF file...
INFO:    Build complete: blah.sif

I was writing tests line by line in the %test section and didn't realise that my tests had failed because the image was built. My solution was then to chain each test using &&, which worked.

Only later I realised that the %test section works like a script when I saw the following example from the documentation:

%test
   #!/bin/zsh

   echo "$(readlink /proc/$$/exe) is our shell"

Therefore, another way to catch errors when using Bash is to use set -e, which will exit when any command in the script fails.

Bootstrap: docker
From: debian:bullseye-slim

%runscript
    exec /usr/local/bin/hello.sh

%post
    printf '#!/usr/bin/env bash\necho Hi there\n' > /usr/local/bin/hello.sh \
    && chmod 755 /usr/local/bin/hello.sh

%test
    /bin/bash
    set -e
    laksdjf
    echo $?
    /usr/local/bin/hello.sh
INFO:    Starting build...
INFO:    Running post scriptlet
+ printf #!/usr/bin/env bash\necho Hi there\n
+ chmod 755 /usr/local/bin/hello.sh
INFO:    Adding runscript
INFO:    Adding testscript
INFO:    Running testscript
/.singularity.d/test: 5: laksdjf: not found
FATAL:   While performing build: failed to execute %test script: exit status 127



Creative Commons License
This work is licensed under a Creative Commons
Attribution 4.0 International License
.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.