Setting up the OCI Runtime Spec test suite
This is Part 7 of the series Building a container runtime from scratch in Go.
In the seventh part of the series we set up a virtual environment and configure the OCI Runtime Spec test suite to test our runtime's support for the OCI Runtime CLI.
The source code to accompany this post is available on GitHub.
The Open Container Initiative1 (OCI) provides a collection of tools for working with the OCI Runtime Specification2. Of particular interest to us, is the runtime validation suite, which can be used to validate container runtimes according to the OCI Runtime Command Line Interface3.
List of runtime-tools tests (click to collapse/expand)
- default
- config_updates_without_affect
- create
- delete
- hooks
- hooks_stdin
- hostname
- kill
- kill_no_effect
- killsig
- linux_devices
- linux_masked_paths
- linux_mount_label
- linux_ns_itype
- linux_ns_nopath
- linux_ns_path
- linux_ns_path_type
- linux_process_apparmor_profile
- linux_readonly_paths
- linux_rootfs_propagation
- linux_seccomp
- linux_sysctl
- linux_uid_mappings
- misc_props
- mounts
- pidfile
- poststart
- poststart_fail
- poststop
- poststop_fail
- prestart
- prestart_fail
- process
- process_capabilities
- process_capabilities_fail
- process_oom_score_adj
- process_rlimits
- process_rlimits_fail
- process_user
- root_readonly_true
- start
- state
- delete_resources
- delete_only_create_resources
- linux_cgroups_blkio
- linux_cgroups_cpus
- linux_cgroups_devices
- linux_cgroups_hugetlb
- linux_cgroups_memory
- linux_cgroups_network
- linux_cgroups_pids
- linux_cgroups_relative_blkio
- linux_cgroups_relative_cpus
- linux_cgroups_relative_devices
- linux_cgroups_relative_hugetlb
- linux_cgroups_relative_memory
- linux_cgroups_relative_network
- linux_cgroups_relative_pids
The runtime tools can be found at https://github.com/opencontainers/runtime-tools but following the instructions doesn’t exactly work. Also, given where we are in the development process of our container runtime, we couldn’t just checkout the repo, build and run the tests, even if it were that simple.
Configuring the OCI Runtime Spec test suite
Running the tests in runtime-tools
is a bit of a dirty process. We’ll create a Bash script to make it a bit easier. I’ll just assume that even if you’re not familiar with Bash, what’s going on here should be pretty easy to follow.
#!/bin/bash
if [[ -z ${RUNTIME} ]]; then
echo "'RUNTIME' variable not set."
exit 1
fi
if [[ -z ./runtimetest ]]; then
echo "'runtimetest' not available."
echo "Try running in 'runtime-tools' directory."
exit 1
fi
logdir=/var/log/runtime-tools
mkdir -p $logdir
# tests to run
tests=(
"create" # run the 'create' tests
)
# run tests
for test in "${tests[@]}"; do
./validation/${test}/${test}.t 2>&1 | tee ${logdir}/${test}.log
done
# check for failures
total_failures=0
for test in "${tests[@]}"; do
failures=$(grep -F "not ok" ${logdir}/${test}.log | wc -l)
if [ 0 -ne $failures ]; then
total_failures=$(($total_failures + $failures))
echo "${test} - $failures"
fi
done
if [ 0 -ne $total_failures ]; then
echo "Total failures: $total_failures"
exit 1
fi
The script can be executed like: sudo RUNTIME=/anocir/anocir test/scripts/oci-validation.sh
It will then run the tests specified in the tests
array (for the moment, that’s just "create"
) against the container runtime specified in the RUNTIME
environment variable. Output of the tests will be logged to /var/log/runtime-tools/<test_name>.log
and results (including a summary of any failures) printed to stdout.
Hold off trying to run this for the time-being, though. First, let’s build a virtual machine to run the tests in.
Building a virtual machine
IMPORTANT
It is important that we’re running tests against our runtime in a virtual machine and not on our main workstation.
Over the next few posts we’ll have partially implemented features that will be making changes to the system as the root user and (in some cases) may make changes that irreversibly break the system.
I’ll try to remember to highlight when that’s happening, but I can’t guarantee it. Just play it safe and always run the tests and the runtime in a VM during development.
For virtualisation, I’m using Vagrant and VirtualBox. Since this is a series of posts about container runtimes, not virtual machines or related tooling, I’m not going to go into detail about either. And, of course, if you’re following along but prefer another virtualisation solution, there’s no reason why you can’t use that instead.
My Vagrantfile
is below. I’ll just highlight the important things that will need to be done regardless of what solution you’re using.
Vagrant.configure("2") do |config|
config.vm.box = "bento/ubuntu-24.04"
config.vm.synced_folder '.', '/anocir'
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
vb.cpus = "2"
end
config.vm.provision "shell", inline: <<-SHELL
set -e -x -o pipefail
# Update package lists and install packages
apt-get update && apt-get install -y git ca-certificates wget make vim gcc libseccomp-dev
# Install go
if ! command -v go 2>&1 >/dev/null; then
wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz -O go.tar.gz
tar -C /usr/local -xzf go.tar.gz
echo "PATH=$PATH:/usr/local/go/bin" >> /etc/environment
fi
# Clone runtime-tools repo
if [ ! -d "runtime-tools" ]; then
git clone https://github.com/opencontainers/runtime-tools.git
fi
# Checkout a specific tree so it's not potentially changing underneath us
git -C runtime-tools checkout f7e3563b0271e5cd52d5c915684ea11ef2779572
# Build runtime tools
(cd runtime-tools && make runtimetest validation-executables)
SHELL
end
When configuring our virtual machine, we want to mount our project directory inside the VM. In my case, that’s at /anocir
.
When provisioning the VM, we (probably) need to install some additional packages. Exactly what those are is going to depend on the distro you’re using in the VM. I’m using Ubuntu, so that’s git
, ca-certificates
, wget
, make
, vim
, gcc
, libseccomp-dev
. Check you have equivalent installed.
Of course, we’ll also need to have Go installed.
The runtime-tools
repo (https://github.com/opencontainers/runtime-tools.git) is where the tests are located, so we’ll need to clone that and checkout the f7e3563b0271e5cd52d5c915684ea11ef2779572
tree. There’s nothing particularly special about that tree; it’s just a stable point so that as-and-when changes go into the repo things aren’t shifting underneath us.
Finally, we can build the runtimetest
and validation-executables
make targets that will be used to run the tests.
Running the tests
You’ll now want to provision and bring up the virtual machine so that we can run the tests inside it. If you’re using Vagrant, just run vagrant up --provision
. Now, connect to the box; with Vagrant, that’s vagrant ssh
.
The moment of truth: build the anocir
binary and run the test script against it.
cd /anocir
go build -o anocir
cd /home/vagrant/runtime-tools
sudo RUNTIME=/anocir/anocir /anocir/test/scripts/oci-integration.sh
TAP version 13
ok 1 - create MUST generate an error if the ID is not provided
---
{
"error": "exit status 1",
"reference": "https://github.com/opencontainers/runtime-spec/blob/v1.1.0/runtime.md#create",
"stderr": "Error: accepts 1 arg(s), received 0\n"
}
...
ok 2 - create MUST create a new container
---
{
"reference": "https://github.com/opencontainers/runtime-spec/blob/v1.1.0/runtime.md#create"
}
...
ok 3 - 'state' MUST return the state of a container
---
{
"container ID": "6f42dc0b-2fb1-4ee2-8ef7-ee371cd5dd49",
"state ID": "6f42dc0b-2fb1-4ee2-8ef7-ee371cd5dd49"
}
...
ok 4 - create MUST generate an error if the ID provided is not unique
---
{
"error": "exit status 1",
"reference": "https://github.com/opencontainers/runtime-spec/blob/v1.1.0/runtime.md#create",
"stderr": "Error: create container: container '6f42dc0b-2fb1-4ee2-8ef7-ee371cd5dd49' exists\n"
}
...
1..4
Assuming everything was set up correctly, we should see output like the above - 4 “ok” test results for the create
operation!
Looking at the list of available tests, we might assume that we could enable tests for hooks
, kill
or others. Unfortunately, the implementation of those tests depends on other features of the container runtime that we haven’t implemented. Those features, and the remaining features to complete our container runtime, are what we’ll start on next…
References
-
https://opencontainers.org/ “Open Container Initiative” ↩︎
-
https://github.com/opencontainers/runtime-spec/blob/main/spec.md “OCI Runtime Specification” ↩︎
-
https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md “OCI Runtime Command Line Interface” ↩︎