nixos/test-driver: Run commands with error handling
Bash's standard behavior of not propagating non-zero exit codes through a pipeline is unexpected and almost universally unwanted. Default to setting `pipefail` for the command being run; it can still be turned off by prefixing the pipeline with `set +o pipefail` if needed. Also, set `errexit` and `nonunset` options to make the first command of consecutive commands separated by `;` fail, and disallow dereferencing unset variables respectively.
This commit is contained in:
parent
f36a65f6e2
commit
b7749c7671
@ -274,8 +274,29 @@ start_all()
|
|||||||
</term>
|
</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Execute a shell command, raising an exception if the exit status is not
|
Execute a shell command, raising an exception if the exit status
|
||||||
zero, otherwise returning the standard output.
|
is not zero, otherwise returning the standard output. Commands
|
||||||
|
are run with <literal>set -euo pipefail</literal> set:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
If several commands are separated by <literal>;</literal>
|
||||||
|
and one fails, the command as a whole will fail.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
For pipelines, the last non-zero exit status will be
|
||||||
|
returned (if there is one, zero will be returned
|
||||||
|
otherwise).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Dereferencing unset variables fail the command.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -441,7 +441,7 @@ class Machine:
|
|||||||
def execute(self, command: str) -> Tuple[int, str]:
|
def execute(self, command: str) -> Tuple[int, str]:
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|
||||||
out_command = "( {} ); echo '|!=EOF' $?\n".format(command)
|
out_command = "( set -euo pipefail; {} ); echo '|!=EOF' $?\n".format(command)
|
||||||
self.shell.send(out_command.encode())
|
self.shell.send(out_command.encode())
|
||||||
|
|
||||||
output = ""
|
output = ""
|
||||||
|
@ -23,15 +23,15 @@ import ./make-test-python.nix ({ pkgs, ... }: {
|
|||||||
with subtest("includeStorePath"):
|
with subtest("includeStorePath"):
|
||||||
with subtest("assumption"):
|
with subtest("assumption"):
|
||||||
docker.succeed("${examples.helloOnRoot} | docker load")
|
docker.succeed("${examples.helloOnRoot} | docker load")
|
||||||
docker.succeed("set -euo pipefail; docker run --rm hello | grep -i hello")
|
docker.succeed("docker run --rm hello | grep -i hello")
|
||||||
docker.succeed("docker image rm hello:latest")
|
docker.succeed("docker image rm hello:latest")
|
||||||
with subtest("includeStorePath = false; breaks example"):
|
with subtest("includeStorePath = false; breaks example"):
|
||||||
docker.succeed("${examples.helloOnRootNoStore} | docker load")
|
docker.succeed("${examples.helloOnRootNoStore} | docker load")
|
||||||
docker.fail("set -euo pipefail; docker run --rm hello | grep -i hello")
|
docker.fail("docker run --rm hello | grep -i hello")
|
||||||
docker.succeed("docker image rm hello:latest")
|
docker.succeed("docker image rm hello:latest")
|
||||||
with subtest("includeStorePath = false; works with mounted store"):
|
with subtest("includeStorePath = false; works with mounted store"):
|
||||||
docker.succeed("${examples.helloOnRootNoStore} | docker load")
|
docker.succeed("${examples.helloOnRootNoStore} | docker load")
|
||||||
docker.succeed("set -euo pipefail; docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello")
|
docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello")
|
||||||
docker.succeed("docker image rm hello:latest")
|
docker.succeed("docker image rm hello:latest")
|
||||||
|
|
||||||
with subtest("Ensure Docker images use a stable date by default"):
|
with subtest("Ensure Docker images use a stable date by default"):
|
||||||
|
@ -119,7 +119,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
|
|||||||
|
|
||||||
with subtest("Setup"):
|
with subtest("Setup"):
|
||||||
result = machine.succeed(
|
result = machine.succeed(
|
||||||
"set -o pipefail; curl -sSf localhost:3000/finalize -X POST -d "
|
"curl -sSf localhost:3000/finalize -X POST -d "
|
||||||
+ "@${payloads.finalize} -H 'Content-Type: application/json' "
|
+ "@${payloads.finalize} -H 'Content-Type: application/json' "
|
||||||
+ "| jq .ok | xargs echo"
|
+ "| jq .ok | xargs echo"
|
||||||
)
|
)
|
||||||
@ -132,7 +132,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
|
|||||||
|
|
||||||
with subtest("Base functionality"):
|
with subtest("Base functionality"):
|
||||||
auth = machine.succeed(
|
auth = machine.succeed(
|
||||||
"set -o pipefail; curl -sSf localhost:3000/graphql -X POST "
|
"curl -sSf localhost:3000/graphql -X POST "
|
||||||
+ "-d @${payloads.login} -H 'Content-Type: application/json' "
|
+ "-d @${payloads.login} -H 'Content-Type: application/json' "
|
||||||
+ "| jq '.[0].data.authentication.login.jwt' | xargs echo"
|
+ "| jq '.[0].data.authentication.login.jwt' | xargs echo"
|
||||||
).strip()
|
).strip()
|
||||||
@ -140,7 +140,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
|
|||||||
assert auth
|
assert auth
|
||||||
|
|
||||||
create = machine.succeed(
|
create = machine.succeed(
|
||||||
"set -o pipefail; curl -sSf localhost:3000/graphql -X POST "
|
"curl -sSf localhost:3000/graphql -X POST "
|
||||||
+ "-d @${payloads.content} -H 'Content-Type: application/json' "
|
+ "-d @${payloads.content} -H 'Content-Type: application/json' "
|
||||||
+ f"-H 'Authorization: Bearer {auth}' "
|
+ f"-H 'Authorization: Bearer {auth}' "
|
||||||
+ "| jq '.[0].data.pages.create.responseResult.succeeded'|xargs echo"
|
+ "| jq '.[0].data.pages.create.responseResult.succeeded'|xargs echo"
|
||||||
|
Loading…
Reference in New Issue
Block a user