Directive Fields
This chapter specifies all of the possible fields for a directive. Most, but not all, of these fields have an obvious mapping to job-spec fields.
filter
This field must be a string, which is interpretted as a test filter
pattern. The directive only applies to tests that match the filter.
If there is no filter
field, the directive applies to all tests.
Sometimes it is useful to use multi-line strings for long patterns:
[[directives]]
filter = """
package.equals(maelstrom-client) ||
package.equals(maelstrom-client-process) ||
package.equals(maelstrom-container) ||
package.equals(maelstrom-fuse) ||
package.equals(maelstrom-util)"""
layers = [{ stubs = ["/tmp/"] }]
mounts = [{ fs_type = "tmp", mount_point = "/tmp" }]
include_shared_libraries
[[directives]]
include_shared_libraries = true
This boolean field sets the include_shared_libraries
job spec psuedo-field.
We call it a psuedo-field because it's not a real field in the job spec, but
instead determines how cargo-maelstrom
will do its post-processing after
computing the job spec from directives.
In post-processing, if the include_shared_libraries
psuedo-field is false,
cargo-maelstrom
will only push a single layer onto the job spec. This layer
will contain the test executable, placed in the root directory.
On the other hand, if the psuedo-field is true, then cargo-maelstrom
will
push two layers onto the job spec. The first will be a layer containing all of
the shared-library dependencies for the test executable. The second will
contain the test executable, placed in the root directory. (Two layers are used
so that the shared-library layer can be cached and used by other tests.)
If the psuedo-field is never set one way or the other, then cargo-maelstrom
will choose a value based on the layers
field of the job spec. In this case,
include_shared_libraries
will be true if and only if layers
is empty.
You usually want this pseudo-field to be true, unless you're using a container image for your tests. In that case, you probably want to use the shared libraries included with the container image, not those from the system running the tests.
image
Sometimes it makes sense to build your test's container from an OCI container
image. For example, when we do integration tests of cargo-maelstrom
, we want
to run in an environment with cargo
installed.
This is what the image
field is for.
[[directives]]
filter = "package.equals(cargo-maelstrom)"
image.name = "rust"
image.use = ["layers", "environment"]
[[directives]]
filter = "package.equals(maelstrom-client) && test.equals(integration_test)"
image = { name = "alpine", use = ["layers", "environment"] }
The image
field must be a table with two subfields: name
and use
.
The name
sub-field specifies the name of the image. It must be a string. This
will be used to find the image on Docker Hub.
The use
sub-field must be a list of strings specifying what parts of the
container image to use for the job spec. It must contain a non-empty subset of:
"layers"
: Thelayers
field of the job spec is replaced by the layers specified in the container image. These layers will betar
layers pointing to the tar files in the container depot."environment"
: Theenvironment
field of the job spec is replaced by the environment specified in the container image."working_directory"
: Theworking_directory
field of the job spec is replaced by the working directory specified in the container image.
In the example above, we specified a TOML table in two different, equivalent ways for illustrative purposes.
layers
[[directives]]
layers = [
{ tar = "layers/foo.tar" },
{ paths = ["layers/a/b.bin", "layers/a/c.bin"], strip_prefix = "layers/a/" },
{ glob = "layers/b/**", strip_prefix = "layers/b/" },
{ stubs = ["/dev/{null, full}", "/proc/"] },
{ symlinks = [{ link = "/dev/stdout", target = "/proc/self/fd/1" }] }
]
This field provides an ordered list of layers for the job spec's
layers
field.
Each element of the list must be a table with one of the following keys:
tar
: The value must be a string, indicating the local path of the tar file. This is used to create a tar layer.paths
: The value must be a list of strings, indicating the local paths of the files and directories to include to create a paths layer. It may also include fields fromprefix_options
(see below).glob
: The value must be a string, indicating the glob pattern to use to create a glob layer. It may also include fields fromprefix_options
(see below).stubs
: The value must be a list of strings. These strings are optionally brace-expanded and used to create a stubs layer.symlinks
: The value must be a list of tables oflink
/target
pairs. These strings are used to create a symlinks layer.
If the layer is a paths
or glob
layer, then the table can have any of the
following extra fields used to provide the
prefix_options
:
follow_symlinks
: A boolean value. Used to specifyfollow_symlinks
.canonicalize
: A boolean value. Used to specifycanonicalize
.strip_prefix
: A string value. Used to specifystrip_prefix
.prepend_prefix
: A string value. Used to specifyprepend_prefix
.
For example:
[[directives]]
layers = [
{ paths = ["layers"], strip_prefix = "layers/", prepend_prefix = "/usr/share/" },
]
This would create a layer containing all of the files and directories
(recursively) in the local layers
subdirectory, mapping local file
layers/example
to /usr/share/example
in the test's container.
This field can't be set in the same directive as image
if the image.use
contains "layers"
.
added_layers
This field is like layers
, except it appends to the job spec's
layers
field instead of replacing it.
This field can be used in the same directive as an image.use
that contains
"layers"
. For example:
[[directives]]
image.name = "cool-image"
image.use = ["layers"]
added_layers = [
{ paths = [ "extra-layers" ], strip_prefix = "extra-layers/" },
]
This directive sets uses the layers from "cool-image"
, but with the contents
of local extra-layers
directory added in as well.
environment
[[directives]]
environment = {
USER = "bob",
RUST_BACKTRACE = "$env{RUST_BACKTRACE:-0}",
}
This field sets the environment
field of
the job spec. It must be a table with string values. It supports two forms of
$
expansion within those string values:
$env{FOO}
evaluates to the value ofcargo-maelstrom
'sFOO
environment variable.$prev{FOO}
evaluates to the previous value ofFOO
for the job spec.
It is an error if the referenced variable doesn't exist. However, you can use
:-
to provide a default value:
FOO = "$env{FOO:-bar}"
This will set FOO
to whatever cargo-maelstrom
's FOO
environment variable
is, or to "bar"
if cargo-maelstrom
doesn't have a FOO
environment
variable.
This field can't be set in the same directive as image
if the image.use
contains "environment"
.
added_environment
This field is like environment
, except it updates the job
spec's environment
field instead of replacing it.
When this is provided in the same directive as the environment
field,
the added_environment
gets evaluated after the environment
field. For example:
[[directives]]
environment = { VAR = "foo" }
[[directives]]
environment = { VAR = "bar" }
added_environment = { VAR = "$prev{VAR}" }
In this case, VAR
will be "bar"
, not "foo"
.
This field can be used in the same directive as an image.use
that contains
"environment"
. For example:
[[directives]]
image = { name = "my-image", use = [ "layers", "environment" ] }
added_environment = { PATH = "/scripts:$prev{PATH}" }
This prepends "/scripts"
to the PATH
provided by the image without changing
any of the other environment variables.
mounts
[[directives]]
mounts = [
{ fs_type = "tmp", mount_point = "/tmp" },
{ fs_type = "proc", mount_point = "/proc" },
{ fs_type = "sys", mount_point = "/sys" },
]
This field sets the mounts
field of
the job spec. It must be a list of tables, each of which must have two fields:
fs_type
: This indicates the type of special file system to mount, and must be one of the following strings:"tmp"
,"proc"
, or"sys"
.mount_point
: This must be a string. It specifies the mount point within the container for the file system.
added_mounts
This field is like mounts
, except it appends to the job spec's
mounts
field instead of replacing it.
devices
[[directives]]
devices = ["fuse", "full", "null", "random", "tty", "urandom", "zero"]
This field sets the devices
field of
the job spec. It must be a list of strings, whose elements must be one of:
"full"
"fuse"
"null"
"random"
"tty"
"urandom"
"zero"
The order of the values doesn't matter, as the list is treated like an unordered set.
added_devices
This field is like devices
, except it inserts into to the job
spec's devices
set instead of replacing it.
working_directory
[[directives]]
working_directory = "/home/root/"
This field sets the
working_directory
field of
the job spec. It must be a string.
This field can't be set in the same directive as image
if the image.use
contains "working_directory"
.
enable_loopback
[[directives]]
enable_loopback = true
This field sets the
enable_loopback
field of
the job spec. It must be a boolean.
enable_writable_file_system
[[directives]]
enable_writable_file_system = true
This field sets the
enable_writable_file_system
field of the job spec. It must be a boolean.
user
[[directives]]
user = 1000
This field sets the user
field of the job
spec. It must be an unsigned, 32-bit integer.
group
[[directives]]
group = 1000
This field sets the group
field of the
job spec. It must be an unsigned, 32-bit integer.
timeout
[[directives]]
timeout = 60
This field sets the timeout
field of the
job spec. It must be an unsigned, 32-bit integer.