1import ../make-test-python.nix (
2 { pkgs, lib, ... }:
3 let
4 quadletContainerFile = pkgs.writeText "quadlet.container" ''
5 [Unit]
6 Description=A test quadlet container
7
8 [Container]
9 Image=localhost/scratchimg:latest
10 Exec=bash -c 'trap exit SIGTERM SIGINT; while true; do sleep 1; done'
11 ContainerName=quadlet
12 Volume=/nix/store:/nix/store
13 Volume=/run/current-system/sw/bin:/bin
14
15 [Install]
16 WantedBy=default.target
17 '';
18 in
19 {
20 name = "podman";
21 meta = {
22 maintainers = lib.teams.podman.members;
23 };
24
25 nodes = {
26 rootful =
27 { pkgs, ... }:
28 {
29 virtualisation.podman.enable = true;
30
31 # hack to ensure that podman built with and without zfs in extraPackages is cached
32 boot.supportedFilesystems = [ "zfs" ];
33 networking.hostId = "00000000";
34 };
35 rootful_norunc =
36 { pkgs, ... }:
37 {
38 virtualisation.podman.enable = true;
39 virtualisation.podman.extraRuntimes = [ ];
40 };
41 rootless =
42 { pkgs, ... }:
43 {
44 virtualisation.podman.enable = true;
45
46 users.users.alice = {
47 isNormalUser = true;
48 };
49 };
50 dns =
51 { pkgs, ... }:
52 {
53 virtualisation.podman.enable = true;
54
55 virtualisation.podman.defaultNetwork.settings.dns_enabled = true;
56 };
57 docker =
58 { pkgs, ... }:
59 {
60 virtualisation.podman.enable = true;
61
62 virtualisation.podman.dockerSocket.enable = true;
63
64 environment.systemPackages = [
65 pkgs.docker-client
66 ];
67
68 users.users.alice = {
69 isNormalUser = true;
70 extraGroups = [ "podman" ];
71 };
72
73 users.users.mallory = {
74 isNormalUser = true;
75 };
76 };
77 };
78
79 testScript = ''
80 import shlex
81
82
83 def su_cmd(cmd, user = "alice"):
84 cmd = shlex.quote(cmd)
85 return f"su {user} -l -c {cmd}"
86
87
88 rootful.wait_for_unit("sockets.target")
89 rootful_norunc.wait_for_unit("sockets.target")
90 rootless.wait_for_unit("sockets.target")
91 dns.wait_for_unit("sockets.target")
92 docker.wait_for_unit("sockets.target")
93 start_all()
94
95 with subtest("Run container as root with runc"):
96 rootful.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
97 rootful.succeed(
98 "podman run --runtime=runc -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
99 )
100 rootful.succeed("podman ps | grep sleeping")
101 rootful.succeed("podman stop sleeping")
102 rootful.succeed("podman rm sleeping")
103
104 with subtest("Run container as root with crun"):
105 rootful.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
106 rootful.succeed(
107 "podman run --runtime=crun -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
108 )
109 rootful.succeed("podman ps | grep sleeping")
110 rootful.succeed("podman stop sleeping")
111 rootful.succeed("podman rm sleeping")
112
113 with subtest("Run container as root with the default backend"):
114 rootful.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
115 rootful.succeed(
116 "podman run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
117 )
118 rootful.succeed("podman ps | grep sleeping")
119 rootful.succeed("podman stop sleeping")
120 rootful.succeed("podman rm sleeping")
121
122 # now without installed runc
123 with subtest("Run runc-less container as root with runc"):
124 rootful_norunc.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
125 rootful_norunc.fail(
126 "podman run --runtime=runc -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
127 )
128
129 with subtest("Run runc-less container as root with crun"):
130 rootful_norunc.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
131 rootful_norunc.succeed(
132 "podman run --runtime=crun -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
133 )
134 rootful_norunc.succeed("podman ps | grep sleeping")
135 rootful_norunc.succeed("podman stop sleeping")
136 rootful_norunc.succeed("podman rm sleeping")
137
138 with subtest("Run runc-less container as root with the default backend"):
139 rootful_norunc.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
140 rootful_norunc.succeed(
141 "podman run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
142 )
143 rootful_norunc.succeed("podman ps | grep sleeping")
144 rootful_norunc.succeed("podman stop sleeping")
145 rootful_norunc.succeed("podman rm sleeping")
146
147 # start systemd session for rootless
148 rootless.succeed("loginctl enable-linger alice")
149 rootless.succeed(su_cmd("whoami"))
150 rootless.sleep(1)
151
152 with subtest("Run container rootless with runc"):
153 rootless.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg"))
154 rootless.succeed(
155 su_cmd(
156 "podman run --runtime=runc -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
157 )
158 )
159 rootless.succeed(su_cmd("podman ps | grep sleeping"))
160 rootless.succeed(su_cmd("podman stop sleeping"))
161 rootless.succeed(su_cmd("podman rm sleeping"))
162
163 with subtest("Run container rootless with crun"):
164 rootless.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg"))
165 rootless.succeed(
166 su_cmd(
167 "podman run --runtime=crun -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
168 )
169 )
170 rootless.succeed(su_cmd("podman ps | grep sleeping"))
171 rootless.succeed(su_cmd("podman stop sleeping"))
172 rootless.succeed(su_cmd("podman rm sleeping"))
173
174 with subtest("Run container rootless with the default backend"):
175 rootless.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg"))
176 rootless.succeed(
177 su_cmd(
178 "podman run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
179 )
180 )
181 rootless.succeed(su_cmd("podman ps | grep sleeping"))
182 rootless.succeed(su_cmd("podman stop sleeping"))
183 rootless.succeed(su_cmd("podman rm sleeping"))
184
185 with subtest("rootlessport"):
186 rootless.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg"))
187 rootless.succeed(
188 su_cmd(
189 "podman run -d -p 9000:8888 --name=rootlessport -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin -w ${pkgs.writeTextDir "index.html" "<h1>Testing</h1>"} scratchimg ${pkgs.python3}/bin/python -m http.server 8888"
190 )
191 )
192 rootless.succeed(su_cmd("podman ps | grep rootlessport"))
193 rootless.wait_until_succeeds(su_cmd("${pkgs.curl}/bin/curl localhost:9000 | grep Testing"))
194 rootless.succeed(su_cmd("podman stop rootlessport"))
195 rootless.succeed(su_cmd("podman rm rootlessport"))
196
197 with subtest("Run container with init"):
198 rootful.succeed(
199 "tar cv -C ${pkgs.pkgsStatic.busybox} . | podman import - busybox"
200 )
201 pid = rootful.succeed("podman run --rm busybox readlink /proc/self").strip()
202 assert pid == "1"
203 pid = rootful.succeed("podman run --rm --init busybox readlink /proc/self").strip()
204 assert pid == "2"
205
206 with subtest("aardvark-dns"):
207 dns.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
208 dns.succeed(
209 "podman run -d --name=webserver -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin -w ${pkgs.writeTextDir "index.html" "<h1>Testing</h1>"} scratchimg ${pkgs.python3}/bin/python -m http.server 8000"
210 )
211 dns.succeed("podman ps | grep webserver")
212 dns.wait_until_succeeds(
213 "podman run --rm --name=client -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg ${pkgs.curl}/bin/curl http://webserver:8000 | grep Testing"
214 )
215 dns.succeed("podman stop webserver")
216 dns.succeed("podman rm webserver")
217
218 with subtest("A podman member can use the docker cli"):
219 docker.succeed(su_cmd("docker version"))
220
221 with subtest("Run container via docker cli"):
222 docker.succeed("tar cv --files-from /dev/null | podman import - scratchimg")
223 docker.succeed(
224 "docker run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin localhost/scratchimg /bin/sleep 10"
225 )
226 docker.succeed("docker ps | grep sleeping")
227 docker.succeed("podman ps | grep sleeping")
228 docker.succeed("docker stop sleeping")
229 docker.succeed("docker rm sleeping")
230
231 with subtest("A podman non-member can not use the docker cli"):
232 docker.fail(su_cmd("docker version", user="mallory"))
233
234 with subtest("A rootless quadlet container service is created"):
235 dir = "/home/alice/.config/containers/systemd"
236 rootless.succeed(su_cmd("tar cv --files-from /dev/null | podman import - scratchimg"))
237 rootless.succeed(su_cmd(f"mkdir -p {dir}"))
238 rootless.succeed(su_cmd(f"cp -f ${quadletContainerFile} {dir}/quadlet.container"))
239 rootless.systemctl("daemon-reload", "alice")
240 rootless.systemctl("start network-online.target")
241 rootless.systemctl("start quadlet", "alice")
242 rootless.wait_until_succeeds(su_cmd("podman ps | grep quadlet"), timeout=20)
243 rootless.systemctl("stop quadlet", "alice")
244
245 # TODO: add docker-compose test
246
247 '';
248 }
249)