projects/guix·blob kernel-updates · refs

1002 lines · 37.6 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2024-2026 Giacomo Leidi <[email protected]>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (gnu tests containers)
20 #:use-module (gnu)
21 #:use-module (gnu tests)
22 #:use-module (guix build-system trivial)
23 #:use-module (gnu packages bash)
24 #:use-module (gnu packages containers)
25 #:use-module (gnu packages databases)
26 #:use-module (gnu packages guile)
27 #:use-module (gnu packages guile-xyz)
28 #:use-module (gnu services)
29 #:use-module (gnu services containers)
30 #:use-module (gnu services databases)
31 #:use-module (gnu services desktop)
32 #:use-module ((gnu services docker)
33 #:select (containerd-service-type
34 docker-service-type))
35 #:use-module (gnu services dbus)
36 #:use-module (gnu services networking)
37 #:use-module (gnu services web)
38 #:use-module (gnu system)
39 #:use-module (gnu system accounts)
40 #:use-module (gnu system vm)
41 #:use-module (guix gexp)
42 #:use-module ((guix licenses) #:prefix license:)
43 #:use-module (guix monads)
44 #:use-module (guix packages)
45 #:use-module (guix profiles)
46 #:use-module ((guix scripts pack) #:prefix pack:)
47 #:use-module (guix store)
48 #:export (%test-rootless-podman
49 %test-rootless-podman-with-least-authority-wrapper
50 %test-oci-service-rootless-podman
51 %test-oci-service-docker))
52
53
54(define %rootless-podman-os
55 (simple-operating-system
56 (service rootless-podman-service-type
57 (rootless-podman-configuration
58 (subgids
59 (list (subid-range (name "dummy"))))
60 (subuids
61 (list (subid-range (name "dummy"))))))
62
63 (service dhcpcd-service-type)
64 (service dbus-root-service-type)
65 (service polkit-service-type)
66 (service elogind-service-type)
67
68 (simple-service 'accounts
69 account-service-type
70 (list (user-account
71 (name "dummy")
72 (group "users")
73 (supplementary-groups '("wheel" "netdev" "cgroup"
74 "audio" "video")))))))
75
76(define %miniflux-create-admin-credentials
77 #~(begin
78 (mkdir "/var/miniflux")
79 (call-with-output-file "/var/miniflux/admin-username"
80 (lambda (port)
81 (display "test" port)))
82 (call-with-output-file "/var/miniflux/admin-password"
83 (lambda (port)
84 (display "testpassword" port)))))
85
86;; A separate operating-system definition is necessary to test the interactions
87;; between rootless podman and least-authority-wrapper backed services, miniflux
88;; in this case. For the purpose of the test, any service using it should be
89;; fine though.
90(define %rootless-podman-os-with-least-authority-wrapper
91 (simple-operating-system
92 (service rootless-podman-service-type
93 (rootless-podman-configuration
94 (subgids
95 (list (subid-range (name "alice"))))
96 (subuids
97 (list (subid-range (name "alice"))))))
98 (service dhcpcd-service-type)
99 (service dbus-root-service-type)
100 (service polkit-service-type)
101 (service elogind-service-type)
102 (simple-service 'create-miniflux-admin-credentials
103 activation-service-type
104 %miniflux-create-admin-credentials)
105 (service postgresql-service-type
106 (postgresql-configuration
107 (postgresql postgresql)))
108 (service miniflux-service-type
109 (miniflux-configuration
110 (listen-address "/var/run/miniflux/miniflux.sock")))))
111
112(define (run-rootless-podman-test oci-tarball)
113
114 (define os
115 (marionette-operating-system
116 (operating-system-with-gc-roots
117 %rootless-podman-os
118 (list oci-tarball))
119 #:imported-modules '((gnu services herd)
120 (guix combinators))))
121
122 (define vm
123 (virtual-machine
124 (operating-system os)
125 (volatile? #f)
126 (memory-size 1024)
127 (disk-image-size (* 3000 (expt 2 20)))
128 (port-forwardings '())))
129
130 (define test
131 (with-imported-modules '((gnu build marionette)
132 (gnu services herd))
133 #~(begin
134 (use-modules (srfi srfi-11) (srfi srfi-64)
135 (gnu build marionette))
136
137 (define marionette
138 ;; Relax timeout to accommodate older systems and
139 ;; allow for pulling the image.
140 (make-marionette (list #$vm) #:timeout 60))
141 (define out-dir "/tmp")
142
143 (test-runner-current (system-test-runner #$output))
144 (test-begin "rootless-podman")
145 (marionette-eval
146 '(begin
147 (use-modules (gnu services herd))
148 (wait-for-service 'file-system-/sys/fs/cgroup))
149 marionette)
150
151 (test-assert "services started successfully and /sys/fs/cgroup has correct permissions"
152 (begin
153 (define (run-test)
154 (marionette-eval
155 `(begin
156 (use-modules (ice-9 popen)
157 (ice-9 match)
158 (ice-9 rdelim))
159
160 (define (read-lines file-or-port)
161 (define (loop-lines port)
162 (let loop ((lines '()))
163 (match (read-line port)
164 ((? eof-object?)
165 (reverse lines))
166 (line
167 (loop (cons line lines))))))
168
169 (if (port? file-or-port)
170 (loop-lines file-or-port)
171 (call-with-input-file file-or-port
172 loop-lines)))
173
174 (define slurp
175 (lambda args
176 (let* ((port (apply open-pipe* OPEN_READ args))
177 (output (read-lines port))
178 (status (close-pipe port)))
179 output)))
180 (let* ((bash
181 (string-append #$bash "/bin/bash"))
182 (response1
183 (slurp bash "-c"
184 (string-append "ls -la /sys/fs/cgroup | "
185 "grep -E ' \\./?$' | awk '{ print $4 }'")))
186 (response2 (slurp bash "-c"
187 (string-append "ls -l /sys/fs/cgroup/cgroup"
188 ".{procs,subtree_control,threads} | "
189 "awk '{ print $4 }' | sort -u"))))
190 (list (string-join response1 "\n") (string-join response2 "\n"))))
191 marionette))
192 ;; Allow services to come up on slower machines
193 (let loop ((attempts 0))
194 (if (= attempts 60)
195 (error "Services didn't come up after more than 60 seconds")
196 (if (equal? '("cgroup" "cgroup")
197 (run-test))
198 #t
199 (begin
200 (sleep 1)
201 (format #t "Services didn't come up yet, retrying with attempt ~a~%"
202 (+ 1 attempts))
203 (loop (+ 1 attempts))))))))
204
205 (test-equal "/sys/fs/cgroup/cgroup.subtree_control content is sound"
206 (list "cpu" "cpuset" "io" "memory" "pids")
207 (marionette-eval
208 `(begin
209 (use-modules (srfi srfi-1)
210 (ice-9 popen)
211 (ice-9 match)
212 (ice-9 rdelim))
213
214 (define (read-lines file-or-port)
215 (define (loop-lines port)
216 (let loop ((lines '()))
217 (match (read-line port)
218 ((? eof-object?)
219 (reverse lines))
220 (line
221 (loop (cons line lines))))))
222
223 (if (port? file-or-port)
224 (loop-lines file-or-port)
225 (call-with-input-file file-or-port
226 loop-lines)))
227
228 (define slurp
229 (lambda args
230 (let* ((port (apply open-pipe* OPEN_READ args))
231 (output (read-lines port))
232 (status (close-pipe port)))
233 output)))
234 (let* ((response1 (slurp
235 ,(string-append #$coreutils "/bin/cat")
236 "/sys/fs/cgroup/cgroup.subtree_control")))
237 (sort-list (string-split (first response1) #\space) string<?)))
238 marionette))
239
240 (test-equal "Load oci image and run it (unprivileged)"
241 '("hello world" "hi!" "JSON!" #o1777)
242 (marionette-eval
243 `(begin
244 (use-modules (srfi srfi-1)
245 (ice-9 popen)
246 (ice-9 match)
247 (ice-9 rdelim))
248
249 (define (wait-for-file file)
250 ;; Wait until FILE shows up.
251 (let loop ((i 60))
252 (cond ((file-exists? file)
253 #t)
254 ((zero? i)
255 (error "file didn't show up" file))
256 (else
257 (pk 'wait-for-file file)
258 (sleep 1)
259 (loop (- i 1))))))
260
261 (define (read-lines file-or-port)
262 (define (loop-lines port)
263 (let loop ((lines '()))
264 (match (read-line port)
265 ((? eof-object?)
266 (reverse lines))
267 (line
268 (loop (cons line lines))))))
269
270 (if (port? file-or-port)
271 (loop-lines file-or-port)
272 (call-with-input-file file-or-port
273 loop-lines)))
274
275 (define slurp
276 (lambda args
277 (let* ((port (apply open-pipe* OPEN_READ
278 (list "sh" "-l" "-c"
279 (string-join
280 args
281 " "))))
282 (output (read-lines port))
283 (status (close-pipe port)))
284 output)))
285
286 (match (primitive-fork)
287 (0
288 (dynamic-wind
289 (const #f)
290 (lambda ()
291 (setgid (passwd:gid (getpwnam "dummy")))
292 (setuid (passwd:uid (getpw "dummy")))
293
294 (let* ((loaded (slurp ,(string-append #$podman
295 "/bin/podman")
296 "load" "-i"
297 ,#$oci-tarball))
298 (repository&tag "localhost/guile-guest:latest")
299 (response1 (slurp
300 ,(string-append #$podman "/bin/podman")
301 "run" "--pull" "never"
302 "--entrypoint" "bin/Guile"
303 repository&tag
304 "/aa.scm"))
305 (response2 (slurp ;default entry point
306 ,(string-append #$podman "/bin/podman")
307 "run" "--pull" "never" repository&tag
308 "-c" "'(display \"hi!\")'"))
309
310 ;; Check whether (json) is in $GUILE_LOAD_PATH.
311 (response3 (slurp ;default entry point + environment
312 ,(string-append #$podman "/bin/podman")
313 "run" "--pull" "never" repository&tag
314 "-c" "'(use-modules (json))
315 (display (json-string->scm (scm->json-string \"JSON!\")))'"))
316
317 ;; Check whether /tmp exists.
318 (response4 (slurp
319 ,(string-append #$podman "/bin/podman")
320 "run" "--pull" "never" repository&tag "-c"
321 "'(display (stat:perms (lstat \"/tmp\")))'")))
322 (call-with-output-file (string-append ,out-dir "/response1")
323 (lambda (port)
324 (display (string-join response1 " ") port)))
325 (call-with-output-file (string-append ,out-dir "/response2")
326 (lambda (port)
327 (display (string-join response2 " ") port)))
328 (call-with-output-file (string-append ,out-dir "/response3")
329 (lambda (port)
330 (display (string-join response3 " ") port)))
331 (call-with-output-file (string-append ,out-dir "/response4")
332 (lambda (port)
333 (display (string-join response4 " ") port)))))
334 (lambda ()
335 (primitive-exit 127))))
336 (pid
337 (cdr (waitpid pid))))
338 (wait-for-file (string-append ,out-dir "/response4"))
339 (append
340 (slurp "cat" (string-append ,out-dir "/response1"))
341 (slurp "cat" (string-append ,out-dir "/response2"))
342 (slurp "cat" (string-append ,out-dir "/response3"))
343 (map string->number (slurp "cat" (string-append ,out-dir "/response4")))))
344 marionette))
345
346 (test-end))))
347
348 (gexp->derivation "rootless-podman-test" test))
349
350(define (build-tarball&run-rootless-podman-test)
351 (mlet* %store-monad
352 ((_ (set-grafting #f))
353 (guile (set-guile-for-build (default-guile)))
354 (guest-script-package ->
355 (package
356 (name "guest-script")
357 (version "0")
358 (source #f)
359 (build-system trivial-build-system)
360 (arguments `(#:guile ,guile-3.0
361 #:builder
362 (let ((out (assoc-ref %outputs "out")))
363 (mkdir out)
364 (call-with-output-file (string-append out "/a.scm")
365 (lambda (port)
366 (display "(display \"hello world\n\")" port)))
367 #t)))
368 (synopsis "Display hello world using Guile")
369 (description "This package displays the text \"hello world\" on the
370standard output device and then enters a new line.")
371 (home-page #f)
372 (license license:public-domain)))
373 (profile (profile-derivation (packages->manifest
374 (list guile-3.0 guile-json-3
375 guest-script-package))
376 #:hooks '()
377 #:locales? #f))
378 (tarball (pack:docker-image
379 "docker-pack" profile
380 #:symlinks '(("/bin/Guile" -> "bin/guile")
381 ("aa.scm" -> "a.scm"))
382 #:extra-options
383 '(#:image-tag "guile-guest")
384 #:entry-point "bin/guile"
385 #:localstatedir? #t)))
386 (run-rootless-podman-test tarball)))
387
388(define (run-rootless-podman-test-with-least-authority-wrapper)
389
390 (define os
391 (marionette-operating-system
392 %rootless-podman-os-with-least-authority-wrapper
393 #:imported-modules '((gnu services herd)
394 (guix combinators))))
395
396 (define vm
397 (virtual-machine
398 (operating-system os)
399 (volatile? #t)
400 (memory-size 1024)
401 (disk-image-size (* 3000 (expt 2 20)))
402 (port-forwardings '())))
403
404 (define test
405 (with-imported-modules '((gnu build marionette)
406 (gnu services herd))
407 #~(begin
408 (use-modules (srfi srfi-11) (srfi srfi-64)
409 (gnu build marionette))
410
411 (define marionette
412 ;; Relax timeout to accommodate older systems and
413 ;; allow for pulling the image.
414 (make-marionette (list #$vm) #:timeout 60))
415
416 (test-runner-current (system-test-runner #$output))
417
418 (test-begin "rootless-podman-with-least-authority-wrapper")
419
420 (test-assert "services start correctly"
421 (marionette-eval
422 '(begin
423 (use-modules (gnu services herd))
424 (wait-for-service 'file-system-/sys/fs/cgroup)
425 (wait-for-service 'miniflux))
426 marionette))
427
428 (test-assert "Check miniflux socket file is created"
429 (wait-for-unix-socket "/var/run/miniflux/miniflux.sock" marionette))
430
431 (test-end))))
432 (gexp->derivation "rootless-podman-with-least-authority-wrapper-test" test))
433
434(define %test-rootless-podman
435 (system-test
436 (name "rootless-podman")
437 (description "Test rootless Podman service.")
438 (value (build-tarball&run-rootless-podman-test))))
439
440(define %test-rootless-podman-with-least-authority-wrapper
441 (system-test
442 (name "rootless-podman-with-least-authority-wrapper")
443 (description "Test rootless Podman service, making sure services based on
444least-authority-wrapper keep working. See
445https://codeberg.org/guix/guix/issues/3233 for an example of how it has broken
446in the past.")
447 (value (run-rootless-podman-test-with-least-authority-wrapper))))
448
449(define %guile-oci-image
450 (oci-image
451 (repository "guile")
452 (value
453 (specifications->manifest '("guile")))
454 (pack-options
455 '(#:symlinks (("/bin" -> "bin"))))))
456
457(define %guile-bash-oci-image
458 (oci-image
459 (repository "guile-bash")
460 (value
461 (specifications->manifest '("guile" "bash-minimal")))
462 (pack-options
463 '(#:symlinks (("/bin" -> "bin"))))))
464
465(define %oci-network
466 (oci-network-configuration (name "my-network")))
467
468(define %oci-volume
469 (oci-volume-configuration (name "my-volume")))
470
471(define %oci-wait-for-file
472 #~(define (wait-for-file file)
473 ;; Wait until FILE shows up.
474 (let loop ((i 6))
475 (cond ((file-exists? file)
476 #t)
477 ((zero? i)
478 (error "file didn't show up" file))
479 (else
480 (pk 'wait-for-file file)
481 (sleep 1)
482 (loop (- i 1)))))))
483
484(define %oci-read-lines
485 #~(define (read-lines file-or-port)
486 (define (loop-lines port)
487 (let loop ((lines '()))
488 (match (read-line port)
489 ((? eof-object?)
490 (reverse lines))
491 (line
492 (loop (cons line lines))))))
493
494 (if (port? file-or-port)
495 (loop-lines file-or-port)
496 (call-with-input-file file-or-port
497 loop-lines))))
498
499(define %oci-slurp
500 #~(define slurp
501 (lambda args
502 (let* ((port
503 (apply open-pipe* OPEN_READ
504 (list "sh" "-l" "-c"
505 (string-join args " "))))
506 (output (read-lines port))
507 (status (close-pipe port)))
508 output))))
509
510(define (%oci-rootless-podman-run commands)
511 #~((use-modules (srfi srfi-1)
512 (ice-9 format)
513 (ice-9 popen)
514 (ice-9 match)
515 (ice-9 rdelim)
516 (gnu build oci-containers))
517
518 #$%oci-wait-for-file
519 #$%oci-read-lines
520 #$%oci-slurp
521
522 (define responses
523 (map
524 (lambda (index)
525 (format #f "/tmp/response_~a" index))
526 (iota (length '#$commands))))
527
528 (match (primitive-fork)
529 (0
530 (begin
531 (setgid (passwd:gid (getpwnam "oci-container")))
532 (setuid (passwd:uid (getpw "oci-container")))
533
534 (let* ((outputs
535 (list #$@commands))
536 (outputs-responses
537 (zip outputs responses)))
538 (for-each
539 (match-lambda
540 ((output response)
541 (call-with-output-file response
542 (lambda (port)
543 (display (string-join output "\n") port)))))
544 outputs-responses))))
545 (pid
546 (cdr (waitpid pid))))
547
548 (for-each wait-for-file responses)
549 (map
550 (lambda (response)
551 (sort (slurp "cat" response) string<=?))
552 responses)))
553
554(define %oci-rootless-podman-os
555 (simple-operating-system
556 (service dhcpcd-service-type)
557 (service dbus-root-service-type)
558 (service polkit-service-type)
559 (service elogind-service-type)
560 (service iptables-service-type)
561 (service rootless-podman-service-type)
562 (extra-special-file "/shared.txt"
563 (plain-file "shared.txt" "hello"))
564 (service oci-service-type
565 (oci-configuration
566 (runtime 'podman)
567 (verbose? #t)))
568 (simple-service 'oci-provisioning
569 oci-service-type
570 (oci-extension
571 (networks
572 (list %oci-network))
573 (volumes
574 (list %oci-volume))
575 (containers
576 (list
577 (oci-container-configuration
578 (provision "first")
579 (image %guile-oci-image)
580 (entrypoint "/bin/guile")
581 (network "my-network")
582 (command
583 '("-c" "(use-modules (web server))
584(define (handler request request-body)
585 (values '((content-type . (text/plain))) \"out of office\"))
586(run-server handler 'http `(#:addr ,(inet-pton AF_INET \"0.0.0.0\")))"))
587 (host-environment
588 '(("VARIABLE" . "value")))
589 (volumes
590 '(("my-volume" . "/my-volume")))
591 (extra-arguments
592 '("--env" "VARIABLE")))
593 (oci-container-configuration
594 (provision "second")
595 (image
596 (oci-image
597 (repository "guile-bash")
598 (value
599 (computed-file "guile-oci.tar.gz"
600 #~(begin
601 (symlink #$%guile-bash-oci-image #$output))))))
602 (entrypoint "/bin/guile")
603 (network "my-network")
604 (command
605 '("-c" "(let l ((c 300))
606(display c)
607(newline)
608(sleep 1)
609(when (positive? c)
610 (l (- c 1))))"))
611 (volumes
612 '(("my-volume" . "/my-volume")
613 ("/shared.txt" . "/shared.txt:ro"))))))))))
614
615(define (run-rootless-podman-oci-service-test)
616 (define os
617 (marionette-operating-system
618 (operating-system-with-gc-roots
619 %oci-rootless-podman-os
620 (list))
621 #:imported-modules '((gnu build oci-containers)
622 (gnu build dbus-service)
623 (gnu services herd)
624 (guix combinators))))
625
626 (define vm
627 (virtual-machine
628 (operating-system os)
629 (volatile? #f)
630 (memory-size 1024)
631 (disk-image-size (* 5000 (expt 2 20)))
632 (port-forwardings '())))
633
634 (define test
635 (with-imported-modules '((gnu build oci-containers)
636 (gnu build dbus-service)
637 (gnu build marionette))
638 #~(begin
639 (use-modules (srfi srfi-1) (srfi srfi-11) (srfi srfi-64)
640 (gnu build dbus-service)
641 (gnu build marionette))
642
643 (define marionette
644 ;; Relax timeout to accommodate older systems and
645 ;; allow for pulling the image.
646 (make-marionette (list #$vm) #:timeout 60))
647
648 (test-runner-current (system-test-runner #$output))
649 (test-begin "rootless-podman-oci-service")
650
651 (marionette-eval
652 '(begin
653 (use-modules (gnu services herd))
654 (wait-for-service 'user-processes))
655 marionette)
656
657 (test-assert "podman-volumes running"
658 (begin
659 (define (run-test)
660 (first
661 (marionette-eval
662 `(begin
663 #$@(%oci-rootless-podman-run
664 #~((oci-object-service-available?
665 "/run/current-system/profile/bin/podman"
666 "volume"
667 '("my-volume")
668 #:verbose? #t))))
669 marionette)))
670 ;; Allow services to come up on slower machines.
671 (with-retries 80 1 (equal? '("my-volume") (run-test)))))
672
673 (test-assert "podman-networks running"
674 (begin
675 (define (run-test)
676 (first
677 (marionette-eval
678 `(begin
679 #$@(%oci-rootless-podman-run
680 #~((oci-object-service-available?
681 "/run/current-system/profile/bin/podman"
682 "network"
683 '("my-network")
684 #:verbose? #t))))
685 marionette)))
686 ;; Allow services to come up on slower machines.
687 (with-retries 80 1 (equal? '("my-network" "podman") (run-test)))))
688
689 (test-assert "image loaded"
690 (begin
691 (define (run-test)
692 (first
693 (marionette-eval
694 `(begin
695 #$@(%oci-rootless-podman-run
696 #~((oci-object-service-available?
697 "/run/current-system/profile/bin/podman"
698 "image"
699 '("localhost/guile:latest")
700 #:format-string "{{.Repository}}:{{.Tag}}"
701 #:verbose? #t))))
702 marionette)))
703 ;; Allow services to come up on slower machines.
704 (with-retries 120 1
705 (equal?
706 '("localhost/guile:latest")
707 (run-test)))))
708
709 (test-assert "passing host environment variables"
710 (begin
711 (define (run-test)
712 (first
713 (marionette-eval
714 `(begin
715 #$@(%oci-rootless-podman-run
716 #~((slurp
717 "/run/current-system/profile/bin/podman"
718 "exec" "first"
719 "/bin/guile" "-c"
720 "'(display (getenv \"VARIABLE\"))'"))))
721 marionette)))
722 ;; Allow services to come up on slower machines.
723 (with-retries 80 1 (equal? '("value") (run-test)))))
724
725 (test-equal "mounting host files"
726 '("hello")
727 (first
728 (marionette-eval
729 `(begin
730 #$@(%oci-rootless-podman-run
731 #~((slurp
732 "/run/current-system/profile/bin/podman"
733 "exec" "second"
734 "/bin/guile" "-c" "'(begin
735(use-modules (ice-9 popen) (ice-9 rdelim))
736(display (call-with-input-file \"/shared.txt\" read-line)))'"))))
737 marionette)))
738
739 (test-equal "read and write to provisioned volumes"
740 '("world")
741 (second
742 (marionette-eval
743 `(begin
744 #$@(%oci-rootless-podman-run
745 #~((slurp
746 "/run/current-system/profile/bin/podman"
747 "exec" "first"
748 "/bin/guile" "-c" "'(begin
749(use-modules (ice-9 popen) (ice-9 rdelim))
750(call-with-output-file \"/my-volume/out.txt\"
751 (lambda (p) (display \"world\" p))))'")
752 (slurp
753 "/run/current-system/profile/bin/podman"
754 "exec" "second"
755 "/bin/guile" "-c" "'(begin
756(use-modules (ice-9 popen) (ice-9 rdelim))
757(display
758 (call-with-input-file \"/my-volume/out.txt\" read-line)))'"))))
759 marionette)))
760
761 (test-equal
762 "can read and write to ports over provisioned network"
763 '("out of office")
764 (first
765 (marionette-eval
766 `(begin
767 #$@(%oci-rootless-podman-run
768 #~((slurp
769 "/run/current-system/profile/bin/podman"
770 "exec" "second"
771 "/bin/guile" "-c" "'(begin
772(use-modules (web client))
773(define-values (response out) (http-get \"http://first:8080\"))
774(display out))'"))))
775 marionette)))
776
777 (test-end))))
778
779 (gexp->derivation "rootless-podman-oci-service-test" test))
780
781(define %test-oci-service-rootless-podman
782 (system-test
783 (name "oci-service-rootless-podman")
784 (description "Test Rootless-Podman backed OCI provisioning service.")
785 (value (run-rootless-podman-oci-service-test))))
786
787(define (%oci-docker-run commands)
788 #~((use-modules (srfi srfi-1)
789 (ice-9 format)
790 (ice-9 popen)
791 (ice-9 match)
792 (ice-9 rdelim)
793 (gnu build oci-containers))
794
795 #$%oci-read-lines
796 #$%oci-slurp
797
798 (let ((outputs (list #$@commands)))
799 (map
800 (lambda (output)
801 (sort output string<=?))
802 outputs))))
803
804(define %oci-docker-os
805 (simple-operating-system
806 (service dhcpcd-service-type)
807 (service dbus-root-service-type)
808 (service polkit-service-type)
809 (service elogind-service-type)
810 (service containerd-service-type)
811 (service docker-service-type)
812 (extra-special-file "/shared.txt"
813 (plain-file "shared.txt" "hello"))
814 (service oci-service-type
815 (oci-configuration
816 (verbose? #t)))
817 (simple-service 'oci-provisioning
818 oci-service-type
819 (oci-extension
820 (networks
821 (list %oci-network))
822 (volumes
823 (list %oci-volume))
824 (containers
825 (list
826 (oci-container-configuration
827 (provision "first")
828 (image %guile-oci-image)
829 (entrypoint "/bin/guile")
830 (network "my-network")
831 (command
832 '("-c" "(use-modules (web server))
833(define (handler request request-body)
834 (values '((content-type . (text/plain))) \"out of office\"))
835(run-server handler 'http `(#:addr ,(inet-pton AF_INET \"0.0.0.0\")))"))
836 (host-environment
837 '(("VARIABLE" . "value")))
838 (volumes
839 '(("my-volume" . "/my-volume")))
840 (extra-arguments
841 '("--env" "VARIABLE")))
842 (oci-container-configuration
843 (provision "second")
844 (image
845 (oci-image
846 (repository "guile-bash")
847 (value
848 (computed-file "guile-oci.tar.gz"
849 #~(begin
850 (symlink #$%guile-bash-oci-image #$output))))))
851 (entrypoint "/bin/guile")
852 (network "my-network")
853 (command
854 '("-c" "(let l ((c 300))
855(display c)
856(newline)
857(sleep 1)
858(when (positive? c)
859 (l (- c 1))))"))
860 (volumes
861 '(("my-volume" . "/my-volume")
862 ("/shared.txt" . "/shared.txt:ro"))))))))))
863
864(define (run-docker-oci-service-test)
865 (define os
866 (marionette-operating-system
867 (operating-system-with-gc-roots
868 %oci-docker-os
869 (list))
870 #:imported-modules '((gnu build oci-containers)
871 (gnu build dbus-service)
872 (gnu services herd)
873 (guix combinators))))
874
875 (define vm
876 (virtual-machine
877 (operating-system os)
878 (volatile? #f)
879 (memory-size 1024)
880 (disk-image-size (* 5000 (expt 2 20)))
881 (port-forwardings '())))
882
883 (define test
884 (with-imported-modules '((gnu build oci-containers)
885 (gnu build dbus-service)
886 (gnu build marionette))
887 #~(begin
888 (use-modules (srfi srfi-1) (srfi srfi-11) (srfi srfi-64)
889 (gnu build dbus-service)
890 (gnu build marionette))
891
892 (define marionette
893 ;; Relax timeout to accommodate older systems and
894 ;; allow for pulling the image.
895 (make-marionette (list #$vm) #:timeout 60))
896
897 (test-runner-current (system-test-runner #$output))
898 (test-begin "docker-oci-service")
899
900 (marionette-eval
901 '(begin
902 (use-modules (gnu services herd))
903 (wait-for-service 'dockerd))
904 marionette)
905
906 (test-assert "docker-volumes running"
907 (begin
908 (define (run-test)
909 (first
910 (marionette-eval
911 `(begin
912 #$@(%oci-docker-run
913 #~((oci-object-service-available?
914 "/run/current-system/profile/bin/docker"
915 "volume"
916 '("my-volume")
917 #:verbose? #t))))
918 marionette)))
919 ;; Allow services to come up on slower machines.
920 (with-retries 80 1 (equal? '("my-volume") (run-test)))))
921
922 (test-assert "docker-networks running"
923 (begin
924 (define (run-test)
925 (first
926 (marionette-eval
927 `(begin
928 #$@(%oci-docker-run
929 #~((oci-object-service-available?
930 "/run/current-system/profile/bin/docker"
931 "network"
932 '("my-network")
933 #:verbose? #t))))
934 marionette)))
935 ;; Allow services to come up on slower machines.
936 (with-retries 80 1 (equal?
937 '("my-network" "none")
938 (run-test)))))
939
940 (test-assert "passing host environment variables"
941 (begin
942 (define (run-test)
943 (first
944 (marionette-eval
945 `(begin
946 #$@(%oci-docker-run
947 #~((slurp
948 "/run/current-system/profile/bin/docker"
949 "exec" "first"
950 "/bin/guile" "-c"
951 "'(display (getenv \"VARIABLE\"))'"))))
952 marionette)))
953 ;; Allow services to come up on slower machines.
954 (with-retries 80 1 (equal? '("value") (run-test)))))
955
956 (test-equal "read and write to provisioned volumes"
957 '("world")
958 (second
959 (marionette-eval
960 `(begin
961 #$@(%oci-docker-run
962 #~((slurp
963 "/run/current-system/profile/bin/docker"
964 "exec" "first"
965 "/bin/guile" "-c" "'(begin
966(use-modules (ice-9 popen) (ice-9 rdelim))
967(call-with-output-file \"/my-volume/out.txt\"
968 (lambda (p) (display \"world\" p))))'")
969 (slurp
970 "/run/current-system/profile/bin/docker"
971 "exec" "second"
972 "/bin/guile" "-c" "'(begin
973(use-modules (ice-9 popen) (ice-9 rdelim))
974(display
975 (call-with-input-file \"/my-volume/out.txt\" read-line)))'"))))
976 marionette)))
977
978 (test-equal
979 "can read and write to ports over provisioned network"
980 '("out of office")
981 (first
982 (marionette-eval
983 `(begin
984 #$@(%oci-docker-run
985 #~((slurp
986 "/run/current-system/profile/bin/docker"
987 "exec" "second"
988 "/bin/guile" "-c" "'(begin (use-modules (web client))
989 (define-values (response out)
990 (http-get \"http://first:8080\"))
991 (display out))'"))))
992 marionette)))
993
994 (test-end))))
995
996 (gexp->derivation "docker-oci-service-test" test))
997
998(define %test-oci-service-docker
999 (system-test
1000 (name "oci-service-docker")
1001 (description "Test Docker backed OCI provisioning service.")
1002 (value (run-docker-oci-service-test))))