Skip to content

Commit d361624

Browse files
authored
Merge pull request #2316 from mazunki/ref-example-nix
Refactor how we build unikernels for testing
2 parents 37a708b + 7fde7e4 commit d361624

File tree

4 files changed

+200
-90
lines changed

4 files changed

+200
-90
lines changed

example.nix

Lines changed: 0 additions & 33 deletions
This file was deleted.

shell.nix

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
# nix-shell --argstr buildpath .
44
buildpath ? "",
55

6-
# The unikernel to build
7-
unikernel ? "./example",
8-
96
# vmrunner path, for vmrunner development
107
vmrunner ? "",
118

@@ -16,7 +13,6 @@
1613
smp ? false,
1714

1815
includeos ? import ./default.nix { inherit withCcache; inherit smp; }
19-
2016
}:
2117

2218
includeos.pkgs.mkShell.override { inherit (includeos) stdenv; } rec {
@@ -48,49 +44,44 @@ includeos.pkgs.mkShell.override { inherit (includeos) stdenv; } rec {
4844
];
4945

5046
shellHook = ''
47+
cat <<-EOF
48+
================================== IncludeOS nix-shell ==================================
49+
Packages:
50+
IncludeOS: ${includeos}
51+
vmrunner: ${vmrunnerPkg}
52+
chainloader: ${includeos.chainloader}
5153
52-
unikernel=$(realpath ${unikernel})
53-
echo -e "Attempting to build unikernel: \n$unikernel"
54-
if [ ! -d "$unikernel" ]; then
55-
echo "$unikernel is not a valid directory"
56-
exit 1
57-
fi
58-
export BUILDPATH=${buildpath}
59-
if [ -z "${buildpath}" ]; then
60-
export BUILDPATH="$(mktemp -d)"
61-
pushd "$BUILDPATH"
62-
else
63-
mkdir -p "$BUILDPATH"
64-
pushd "$BUILDPATH"
65-
fi
66-
cmake "$unikernel" -DARCH=x86_64 -DINCLUDEOS_PACKAGE=${includeos} -DCMAKE_MODULE_PATH=${includeos}/cmake \
67-
-DFOR_PRODUCTION=OFF
68-
make -j $NIX_BUILD_CORES
69-
echo -e "\n====================== IncludeOS nix-shell ====================="
70-
if [ -z "${buildpath}" ]; then
71-
echo -e "\nWorking directory, generated by this script:"
72-
echo $BUILDPATH
73-
echo -e "\nTo use another directory pass in 'buildpath' to nix:"
74-
echo "nix-shell --argstr buildpath you/build/path"
75-
fi
76-
echo -e "\nThe C++ compiler set to:"
77-
echo $(which $CXX)
78-
echo -e "\nIncludeOS package:"
79-
echo ${includeos}
80-
echo -e "\n---------------------- Network privileges ---------------------"
81-
echo "The vmrunner for IncludeOS tests requires bridged networking for full functionality."
82-
echo "The following commands requiring sudo privileges can be used to set this up:"
83-
echo "1. the qemu-bridge-helper needs sudo to create a bridge. Can be enabled with:"
84-
echo " sudo chmod u+s ${includeos.pkgs.qemu}/libexec/qemu-bridge-helper"
85-
echo "2. bridge43 must exist. Can be set up with vmrunner's create_bridge.sh script:"
86-
echo " ${vmrunnerPkg.create_bridge}"
87-
echo "3. /etc/qemu/bridge.conf must contain this line:"
88-
echo " allow bridge43"
89-
echo ""
90-
echo "Some tests require ping, which requires premissions to send raw packets. On some hosts"
91-
echo "this is not enabled by default for iputils provided by nix. It can be enabled with:"
92-
echo "4. sudo setcap cap_net_raw+ep ${includeos.pkgs.iputils}/bin/ping"
93-
echo " "
94-
echo
54+
Tooling:
55+
CXX $(command -v $CXX)
56+
cmake: $(command -v cmake)
57+
nasm: $(command -v nasm)
58+
qemu-system-x86: $(command -v qemu-system-x86_64)
59+
grub-mkrescue: $(command -v grub-mkrescue)
60+
xorriso: $(command -v xorriso)
61+
ping: $(command -v ping)
62+
63+
---------------------------------- Network privileges ----------------------------------
64+
The vmrunner for IncludeOS tests requires bridged networking for full functionality.
65+
The following checklist can be used to set this up from the host:
66+
67+
1. The qemu-bridge-helper needs root escalation to manipulate bridges. You can provide this
68+
either through capabilities or through root execution. Pick one:
69+
sudo chmod u+s ${includeos.pkgs.qemu}/libexec/qemu-bridge-helper
70+
sudo setcap cap_net_admin+ep ${includeos.pkgs.qemu}/libexec/qemu-bridge-helper
71+
72+
2. bridge43 must exist. Can be set up with vmrunner's create_bridge.sh script (not as root):
73+
${vmrunnerPkg.create_bridge}
74+
75+
3. /etc/qemu/bridge.conf must contain this line:
76+
allow bridge43
77+
Also note that /etc/qemu needs specific permissions, so it might be easiest to install
78+
qemu on the host to generate these directories for you, despite not using its executable here.
79+
80+
4. Some tests also perform ICMP pings, which requires permissions to send raw packets. On some
81+
hosts this is not enabled by default for iputils provided by nix.
82+
It can be enabled with:
83+
sudo setcap cap_net_raw+ep ${includeos.pkgs.iputils}/bin/ping
84+
85+
EOF
9586
'';
9687
}

test.sh

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,23 @@ build_chainloader(){
6767
}
6868

6969
build_example(){
70-
nix-build $CCACHE_FLAG example.nix
70+
nix-build $CCACHE_FLAG unikernel.nix
7171
}
7272

7373
multicore_subset(){
74-
nix-shell --pure --arg smp true $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/smp --run ./test.py
74+
nix-build ./unikernel.nix --arg smp true $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/smp --arg doCheck true
7575

7676
# The following tests are not using multiple CPU's, but have been equippedd with some anyway
7777
# to make sure core functionality is not broken by missing locks etc. when waking up more cores.
78-
nix-shell --pure --arg smp true $CCACHE_FLAG --argstr unikernel ./test/net/integration/udp --run ./test.py
79-
nix-shell --pure --arg smp true $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/paging --run ./test.py
78+
nix-shell ./unikernel.nix --arg smp true $CCACHE_FLAG --argstr unikernel ./test/net/integration/udp --arg doCheck true
79+
nix-build ./unikernel.nix --arg smp true $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/paging --arg doCheck true
8080
}
8181

8282
smoke_tests(){
83-
nix-shell --pure $CCACHE_FLAG --argstr unikernel ./test/net/integration/udp --run ./test.py
84-
nix-shell --pure $CCACHE_FLAG --argstr unikernel ./test/net/integration/tcp --run ./test.py
85-
nix-shell --pure $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/paging --run ./test.py
86-
nix-shell --pure $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/smp --run ./test.py
83+
nix-build ./unikernel.nix $CCACHE_FLAG --argstr unikernel ./test/net/integration/udp --arg doCheck true
84+
nix-build ./unikernel.nix $CCACHE_FLAG --argstr unikernel ./test/net/integration/tcp --arg doCheck true
85+
nix-build ./unikernel.nix $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/paging --arg doCheck true
86+
nix-build ./unikernel.nix $CCACHE_FLAG --argstr unikernel ./test/kernel/integration/smp --arg doCheck true
8787
}
8888

8989
run unittests "Build and run unit tests"
@@ -137,21 +137,31 @@ run_testsuite() {
137137

138138
for subfolder in "$base_folder"/*/; do
139139
local skip=false
140+
local sandboxed=true
140141

141142
for exclude in "${exclusion_list[@]}"; do
142143
if [[ "$subfolder" == *"$exclude"* ]]; then
143144
skip=true
144145
break
145146
fi
146147
done
147-
148148
if [ "$skip" = true ]; then
149149
continue
150150
fi
151151

152+
for unsandbox in "${unsandbox_list[@]}"; do
153+
if [[ "$subfolder" == *"$unsandbox"* ]]; then
154+
sandboxed=false
155+
break
156+
fi
157+
done
152158

153159
# The command to run, as string to be able to print the fully expanded command
154-
cmd="nix-shell --pure $CCACHE_FLAG --argstr unikernel $subfolder --run ./test.py"
160+
if $sandboxed; then
161+
cmd="nix-build ./unikernel.nix $CCACHE_FLAG --argstr unikernel ${subfolder%/} --arg doCheck true"
162+
else
163+
cmd="nix-shell ./unikernel.nix $CCACHE_FLAG --argstr unikernel ${subfolder%/} --arg doCheck true"
164+
fi
155165

156166
echo ""
157167
echo "🚧 Step $steps.$substeps"
@@ -194,7 +204,11 @@ exclusions=(
194204
"modules" # Requires 32-bit build, which our shell.nix is not set up for
195205
)
196206

207+
unsandbox_list=(
208+
"term" # fails to create the tun device, like the net integration tests
209+
)
197210
run_testsuite "./test/kernel/integration" "${exclusions[@]}"
211+
unsandbox_list=()
198212

199213
#
200214
# C++ STL runtime tests
@@ -223,7 +237,20 @@ exclusions=(
223237
"websocket" # Linking fails, undefined ref to http_parser_parse_url, http_parser_execute
224238
)
225239

240+
# all the following fail with the following error:
241+
# <vm> failed to create tun device: Operation not permitted
242+
# <vm> qemu-system-x86_64: -netdev bridge,id=net0,br=bridge43: bridge helper failed
243+
unsandbox_list=(
244+
"./test/net/integration/configure"
245+
"./test/net/integration/icmp"
246+
"./test/net/integration/icmp6"
247+
"./test/net/integration/slaac"
248+
"./test/net/integration/tcp"
249+
"./test/net/integration/udp"
250+
"./test/net/integration/dns" # except this one which times out instead
251+
)
226252
run_testsuite "./test/net/integration" "${exclusions[@]}"
253+
unsandbox_list=()
227254

228255
echo -e "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
229256

unikernel.nix

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
{
2+
# The unikernel to build
3+
unikernel ? ./example,
4+
5+
# The test file to run
6+
test ? "test.py",
7+
8+
# Boot unikernel after building it
9+
doCheck ? false,
10+
11+
# Which architecture to build against
12+
arch ? "x86_64",
13+
14+
# Enable multicore suport.
15+
smp ? false,
16+
17+
# Enable ccache support. See overlay.nix for details.
18+
withCcache ? false,
19+
20+
# Enable stricter requirements
21+
forProduction ? false,
22+
23+
# The includeos library to build and link against
24+
includeos ? import ./default.nix { inherit withCcache; inherit smp; },
25+
26+
# vmrunner path, for vmrunner development
27+
vmrunner ? "",
28+
}:
29+
let
30+
absolutePathOf = base: p:
31+
if p == null then null else
32+
if builtins.isPath p then p
33+
else builtins.toPath (base + "/${p}");
34+
35+
unikernelPath = absolutePathOf ./. unikernel;
36+
vmrunnerPkg =
37+
if vmrunner == "" then
38+
includeos.vmrunner
39+
else
40+
includeos.pkgs.callPackage (builtins.toPath /. + vmrunner) {};
41+
in
42+
includeos.stdenv.mkDerivation rec {
43+
pname = "includeos_example";
44+
version = "dev";
45+
src = includeos.pkgs.lib.cleanSource unikernelPath;
46+
dontStrip = true;
47+
inherit doCheck;
48+
49+
nativeBuildInputs = [
50+
includeos.pkgs.buildPackages.nasm
51+
includeos.pkgs.buildPackages.cmake
52+
];
53+
54+
buildInputs = [
55+
includeos
56+
includeos.chainloader
57+
];
58+
59+
cmakeFlags = [
60+
"-DARCH=${arch}"
61+
"-DINCLUDEOS_PACKAGE=${includeos}"
62+
"-DCMAKE_MODULE_PATH=${includeos}/cmake"
63+
"-DFOR_PRODUCTION=${if forProduction then "ON" else "OFF"}"
64+
];
65+
66+
installPhase = ''
67+
runHook preInstall
68+
# we want to place any files required by the test into the output
69+
find -mindepth 1 -maxdepth 1 -type f -exec install -v -D -t "$out/" {} \;
70+
71+
# especially the unikernel image, in case it wasn't at the rootdir already
72+
find -mindepth 2 -name '*.elf.bin' -exec install -v -t "$out/" {} \;
73+
runHook postInstall
74+
'';
75+
76+
77+
nativeCheckInputs = [
78+
includeos.vmrunner
79+
includeos.pkgs.grub2
80+
includeos.pkgs.python3
81+
includeos.pkgs.qemu
82+
includeos.pkgs.iputils
83+
includeos.pkgs.xorriso
84+
];
85+
86+
checkInputs = [
87+
includeos.lest
88+
];
89+
90+
# use `nix-build --arg doCheck true` to run tests normally
91+
checkPhase = ''
92+
runHook preCheck
93+
set -e
94+
if [ -e "${src}/${test}" ]; then
95+
echo "Running IncludeOS test: ${src}/${test}"
96+
python3 "${src}/${test}"
97+
else
98+
echo "Default test script '${test}', but no test was found 😟"
99+
echo "For a custom path, consider specifying the path to the test script:"
100+
echo " --argstr test 'path/to/test.py'"
101+
exit 1
102+
fi
103+
runHook postCheck
104+
'';
105+
106+
# this is a hack
107+
# some tests need to be run through a shell because of net_cap_raw+ep and net_cap_admin+ep
108+
# replace nix-build with nix-shell to test without dropping capabilities
109+
packages = [
110+
(includeos.pkgs.python3.withPackages (p: [
111+
vmrunnerPkg
112+
]))
113+
];
114+
shellHook = ''
115+
set -eu
116+
pkg="$(nix-build ./unikernel.nix --arg doCheck false --arg unikernel ${unikernel})"
117+
118+
testPath="$(realpath "${unikernel}/${test}")"
119+
cd "$pkg"
120+
"$testPath" || exit 1
121+
122+
exit 0
123+
'';
124+
125+
}

0 commit comments

Comments
 (0)